<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no"><title>Dubbo 源码分析 – 服务调用过程 | 云少IT</title><meta name="keywords" content="Dubbo 源码分析 – 服务调用过程"><meta name="author" content="云少"><meta name="copyright" content="云少"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="mobile-web-app-capable" content="yes"><meta name="apple-touch-fullscreen" content="yes"><meta name="apple-mobile-web-app-title" content="Dubbo 源码分析 – 服务调用过程"><meta name="application-name" content="Dubbo 源码分析 – 服务调用过程"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="#ffffff"><meta property="og:type" content="article"><meta property="og:title" content="Dubbo 源码分析 – 服务调用过程"><meta property="og:url" content="https://it985.github.io/posts/2ad0f5ed.html"><meta property="og:site_name" content="云少IT"><meta property="og:description" content="九、Dubbo 源码分析 – 服务调用过程1. 简介在前面的文章中，我们分析了 Dubbo SPI、服务导出与引入、以及集群容错方面的代码。经过前文的铺垫，本篇文章我们终于可以分析服务调用过程了。Dubbo 服务调用过程比较复杂，包含众多步骤。比如发送请求、编解码、服务降级、过滤器链处理、序列化、线"><meta property="og:locale" content="zh-CN"><meta property="og:image" content="https://www.bing.com/th?id=OHR.MandelaCamden_EN-GB1953677756_UHD.jpg"><meta property="article:author" content="云少"><meta property="article:tag" content="云少IT,IT,技术,分享,程序员,博客,教程,工具,框架,bug,java,spring,数据库,"><meta name="twitter:card" content="summary"><meta name="twitter:image" content="https://www.bing.com/th?id=OHR.MandelaCamden_EN-GB1953677756_UHD.jpg"><meta name="description" content="九、Dubbo 源码分析 – 服务调用过程1. 简介在前面的文章中，我们分析了 Dubbo SPI、服务导出与引入、以及集群容错方面的代码。经过前文的铺垫，本篇文章我们终于可以分析服务调用过程了。Dubbo 服务调用过程比较复杂，包含众多步骤。比如发送请求、编解码、服务降级、过滤器链处理、序列化、线"><link rel="shortcut icon" href="/img/logo.webp"><link rel="canonical" href="https://it985.github.io/posts/2ad0f5ed"><link rel="preconnect" href="//npm.elemecdn.com"><link rel="preconnect" href="//npm.onmicrosoft.cn"><link rel="preconnect" href="//www.google-analytics.com" crossorigin=""><link rel="preconnect" href="//busuanzi.ibruce.info"><meta name="google-site-verification" content="NuBZ4r-QCqSgo4XUScdEsQW0bolIHEiVGq4A16ndPQA"><meta name="baidu-site-verification" content="code-xxx"><meta name="msvalidate.01" content="xxx"><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.cbd.int/@fortawesome/fontawesome-free@6.4.0/css/all.min.css" media="print" onload='this.media="all"'><link rel="stylesheet" href="https://cdn.cbd.int/node-snackbar@0.1.16/dist/snackbar.min.css" media="print" onload='this.media="all"'><link rel="stylesheet" href="https://cdn.cbd.int/@fancyapps/ui@5.0.20/dist/fancybox/fancybox.css" media="print" onload='this.media="all"'><link rel="stylesheet" href="https://npm.elemecdn.com/anzhiyu-theme-static@1.0.0/swiper/swiper.min.css" media="print" onload='this.media="all"'><script async src="https://www.googletagmanager.com/gtag/js?id=G-3VMKW5TZBM"></script><script>function gtag(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],gtag("js",new Date),gtag("config","G-3VMKW5TZBM")</script><script>const GLOBAL_CONFIG={linkPageTop:{enable:!0,title:"与数百名博主无限进步",addFriendPlaceholder:"昵称（请勿包含博客等字样）：\n网站地址（要求博客地址，请勿提交个人主页）：\n头像图片url（请提供尽可能清晰的图片，我会上传到我自己的图床）：\n描述：\n站点截图（可选）：\n"},peoplecanvas:void 0,postHeadAiDescription:{enable:!0,gptName:"云AI",mode:"tianli",switchBtn:!1,btnLink:"https://afdian.net/item/886a79d4db6711eda42a52540025c377",randomNum:3,basicWordCount:1e3,key:"48580d1e3f53ae174a1e",Referer:"https://blog.tryrun.top"},diytitle:{enable:!0,leaveTitle:"w(ﾟДﾟ)w 不要走！再看看嘛！",backTitle:"♪(^∇^*)欢迎肥来！"},LA51:{enable:!0,ck:"JiFOrFoQklEn9YLS",LingQueMonitorID:"JiqlTmdeI4e1fPbd"},greetingBox:{enable:!0,default:"晚上好👋",list:[{greeting:"晚安😴",startTime:0,endTime:5},{greeting:"早上好鸭👋, 祝你一天好心情！",startTime:6,endTime:9},{greeting:"上午好👋, 状态很好，鼓励一下～",startTime:10,endTime:10},{greeting:"11点多啦, 在坚持一下就吃饭啦～",startTime:11,endTime:11},{greeting:"午安👋, 宝贝",startTime:12,endTime:14},{greeting:"🌈充实的一天辛苦啦！",startTime:14,endTime:18},{greeting:"19点喽, 奖励一顿丰盛的大餐吧🍔。",startTime:19,endTime:19},{greeting:"晚上好👋, 在属于自己的时间好好放松😌~",startTime:20,endTime:24}]},twikooEnvId:"https://twikoo.tryrun.top/",commentBarrageConfig:void 0,root:"/",preloader:{source:2},friends_vue_info:{apiurl:"https://friends.tryrun.top/"},navMusic:!1,mainTone:void 0,authorStatus:{skills:["🤖️ 数码科技爱好者","🔍 分享与热心帮助","🏠 智能家居小能手","🔨 设计开发一条龙","🤝 专修交互与设计","🏃 脚踏实地行动派","🧱 团队小组发动机","💢 壮汉人狠话不多"]},algolia:{appId:"T5VW6VDYLS",apiKey:"227bcb041816af13cb1698db15a8ac89",indexName:"hexo-blog",hits:{per_page:6},languages:{input_placeholder:"输入关键词后按下回车查找",hits_empty:"找不到您查询的内容：${query}",hits_stats:"找到 ${hits} 条结果，用时 ${time} 毫秒"}},localSearch:void 0,translate:{defaultEncoding:2,translateDelay:0,msgToTraditionalChinese:"繁",msgToSimplifiedChinese:"简",rightMenuMsgToTraditionalChinese:"转为繁体",rightMenuMsgToSimplifiedChinese:"转为简体"},noticeOutdate:{limitDay:365,position:"top",messagePrev:"It has been",messageNext:"days since the last update, the content of the article may be outdated."},highlight:{plugin:"highlighjs",highlightCopy:!0,highlightLang:!0,highlightHeightLimit:330},copy:{success:"复制成功",error:"复制错误",noSupport:"浏览器不支持"},relativeDate:{homepage:!0,simplehomepage:!1,post:!0},runtime:"天",date_suffix:{just:"刚刚",min:"分钟前",hour:"小时前",day:"天前",month:"个月前"},copyright:void 0,lightbox:"fancybox",Snackbar:{chs_to_cht:"你已切换为繁体",cht_to_chs:"你已切换为简体",day_to_night:"你已切换为深色模式",night_to_day:"你已切换为浅色模式",bgLight:"#425AEF",bgDark:"#1f1f1f",position:"top-center"},source:{justifiedGallery:{js:"https://cdn.cbd.int/flickr-justified-gallery@2.1.2/dist/fjGallery.min.js",css:"https://cdn.cbd.int/flickr-justified-gallery@2.1.2/dist/fjGallery.css"}},isPhotoFigcaption:!1,islazyload:!0,isAnchor:!1,shortcutKey:void 0,autoDarkmode:!0}</script><script id="config-diff">var GLOBAL_CONFIG_SITE={configTitle:"云少IT",title:"Dubbo 源码分析 – 服务调用过程",postAI:"true",pageFillDescription:"九、Dubbo 源码分析 – 服务调用过程, 1. 简介, 2. 源码分析, 2.1 服务调用方式, 2.2 服务消费方发送请求, 2.2.1 发送请求, 2.2.2 请求编码, 2.3 服务提供方接收请求, 2.3.1 请求解码, 2.3.2 调用服务, 2.3.2.1 线程派发模型, 2.3.2.2 调用服务, 2.4 服务提供方响应请求, 2.5 服务消费方接收调用结果, 2.5.1 响应数据解码, 2.5.2 向用户线程传递调用结果, 3. 总结九源码分析服务调用过程简介在前面的文章中我们分析了服务导出与引入以及集群容错方面的代码经过前文的铺垫本篇文章我们终于可以分析服务调用过程了服务调用过程比较复杂包含众多步骤比如发送请求编解码服务降级过滤器链处理序列化线程派发以及响应请求等步骤限于篇幅原因本篇文章无法对所有的步骤一一进行分析本篇文章将会重点分析请求的发送与接收编解码线程派发以及响应的发送与接收等过程至于服务降级过滤器链和序列化大家自行进行分析也可以将其当成一个黑盒暂时忽略也没关系介绍完本篇文章要分析的内容接下来我们进入正题吧源码分析在进行源码分析之前我们先来了解服务调用过程首先服务消费者通过代理对象发起远程调用接着通过网络客户端将编码后的请求发送给服务提供方的网络层上也就是在收到请求后首先要做的事情是对数据包进行解码然后将解码后的请求发送至分发器再由分发器将请求派发到指定的线程池上最后由线程池调用具体的服务这就是一个远程调用请求的发送与接收过程至于响应的发送与接收过程这张图中没有表现出来对于这两个过程我们也会进行详细分析服务调用方式支持同步和异步两种调用方式其中异步调用还可细分为有返回值的异步调用和无返回值的异步调用所谓无返回值异步调用是指服务消费方只管调用但不关心调用结果此时会直接返回一个空的若要使用异步特性需要服务消费方手动进行配置默认情况下使用同步调用方式本节以及其他章节将会使用官方提供的分析整个调用过程下面我们从接口的代理类开始进行分析默认使用框架为服务接口生成动态代理类因此我们需要先将代理类进行反编译才能看到源码这里使用阿里开源应用诊断工具反编译代理类结果如下反编译步骤启动输入编号选择进程启动后会打印应用进程列表比如这里输入编号让关联到启动类为的进程上由于项目中只有一个服务接口因此此接口的代理类类名为此时使用命令搜索这个类名使用命令反编译更多使用方法请参考官方文档方法数组将参数存储到数组中调用实现类的方法得到调用结果返回调用结果回声测试方法如上代理类的逻辑比较简单首先将运行时参数存储到数组中然后调用接口实现类的方法得到调用结果最后将结果转型并返回给调用方关于代理类的逻辑就说这么多继续向下分析拦截定义在类中的方法未被子类重写比如如果和等方法被子类重写了这里也直接调用将和封装到中并执行后续的调用中的成员变量类型为内部封装了服务降级逻辑下面简单看一下获取配置值无逻辑直接调用其他对象的方法比如直接执行逻辑不发起远程调用表示消费方对调用服务失败后再执行逻辑不抛出异常调用其他对象的方法调用失败执行逻辑省略其他方法服务降级不是本文重点因此这里就不分析方法了考虑到前文已经详细分析过因此本节略过直接分析设置设置添加到变量中设置异步信息到中抽象方法由子类实现省略其他方法上面的代码来自类其中大部分代码用于添加信息到变量中添加完毕后调用执行后续的调用是一个抽象方法需要由子类实现下面到中看一下设置和到中从数组中获取获取异步配置为表示单向通信异步无返回值发送请求设置上下文中的为返回一个空的异步有返回值发送请求获得实例设置到上下文中暂时返回一个空结果同步调用发送请求得到一个实例并调用该实例的方法进行等待省略其他方法上面的代码包含了对同步和异步调用的处理逻辑搞懂了上面的代码会对的同步和异步调用方式有更深入的了解实现同步和异步调用比较关键的一点就在于由谁调用的方法同步调用模式下由框架自身调用的方法异步调用模式下则由用户调用该方法是一个接口下面我们来看一下它的默认实现类的源码获取请求这个很重要后面还会见到存储映射关系到中循环检测服务提供方是否成功返回了调用结果如果调用结果尚未返回这里等待一段时间如果调用结果成功返回或等待超时此时跳出循环执行后续的逻辑如果调用结果仍未返回则抛出超时异常返回调用结果通过检测字段为空与否判断是否收到了调用结果如果调用结果的状态为则表示调用过程正常服务提供方成功返回了调用结果抛出异常省略其他方法如上当服务消费者还未接收到调用结果时用户线程调用方法会被阻塞住同步调用模式下框架获得对象后会立即调用方法进行等待而异步模式下则是将该对象封装到实例中并将实例设置到中供用户使用是一个适配器用于将中的与中的进行适配这样当用户线程调用的方法时经过适配最终会调用实现类对象的方法也就是的方法到这里关于几种调用方式的代码逻辑就分析完了下面来分析请求数据的发送与接收以及响应数据的发送与接收过程服务消费方发送请求发送请求本节我们来看一下同步调用模式下服务消费方是如何发送调用请求的服务消费方发送请求过程的部分调用栈略为复杂可以看出经过多次调用后才将请求数据送至这样做的原因是通过层为框架引入和语义这一点会在接下来的源码分析过程中会看到其他的就不多说了下面开始进行分析首先分析的源码引用计数自增直接调用被装饰对象的同签名方法直接调用被装饰对象的同签名方法引用计数自增该方法由外部调用自增自减省略部分方法内部定义了一个引用计数变量每当该对象被引用一次都会进行自增每当方法被调用时进行自减内部仅实现了一个引用计数的功能其他方法并无复杂逻辑均是直接调用被装饰对象的相关方法所以这里就不多说了继续向下分析这次是创建对象以下代码均与心跳检测逻辑有关开启心跳检测定时器直接对象的同签名方法直接对象的同签名方法停止心跳检测定时器省略部分方法中很多方法只有一行代码即调用对象的同签名方法那有什么用处呢答案是封装了一些关于心跳检测的逻辑心跳检测并非本文所关注的点因此就不多说了继续向下看这里的指向的是创建对象设置双向通信标志为这里的变量类型为创建对象调用的方法发送请求返回对象到这里大家终于看到了语义了上面的方法首先定义了一个对象然后再将该对象传给的方法进行后续的调用需要说明的是中并未实现方法该方法继承自父类下面直接分析的代码该方法由类实现省略其他方法获取是一个抽象方法具体由子类实现继续向下调用省略其他方法默认情况下使用作为底层的通信框架因此下面我们到类中看一下方法的实现逻辑这里的全限定名称为获取一个类型对象私有构造方法尝试从集合中获取实例如果则创建一个新的实例将键值对存入集合中获取到实例后即可进行后续的调用下面看一下的方法发送消息包含请求和响应消息的值源于中的配置值有两种配置值等待消息发出消息发送失败将抛出异常不等待消息发出将消息放入队列即刻返回默认情况下等待消息发出若在规定时间没能发出会被置为若为这里抛出异常经历多次调用到这里请求数据的发送过程就结束了过程漫长为了便于大家阅读代码这里以为例将方法的整个调用路径贴出来包含多个调用在中出站数据在发出之前还需要进行编码操作接下来我们来分析一下请求数据的编码逻辑请求编码在分析请求编码逻辑之前我们先来看一下数据包结构数据包分为消息头和消息体消息头用于存储一些元信息比如魔数数据包类型消息体长度等消息体中用于存储具体的调用消息比如方法名称参数列表等下面简单列举一下消息头的内容偏移量字段取值魔数高位魔数低位数据包类型调用方式仅在第位被设为的情况下有效单向调用双向调用事件标识当前数据包是请求或响应包当前数据包是心跳包序列化器编号状态请求编号共字节运行时生成消息体长度运行时计算了解了数据包格式接下来我们就可以探索编码过程了这次我们开门见山直接分析编码逻辑所在类如下消息头长度魔数内容对对象进行编码对对象进行编码后面分析创建消息头字节数组长度为设置魔数设置数据包类型和序列化器编号设置通信方式单向双向设置事件标识设置请求编号个字节从第个字节开始设置获取当前的写位置更新为消息头预留个字节的空间创建序列化器比如对事件数据进行序列化操作对请求数据进行序列化操作获取写入的字节数也就是消息体长度将消息体长度写入到消息头中将指针移动到为写消息头做准备从下标处写入消息头设置新的原写下标消息头长度消息体长度省略其他方法以上就是请求对象的编码过程该过程首先会通过位运算将消息头写入到数组中然后对对象的字段执行序列化操作序列化后的数据最终会存储到中序列化操作执行完后可得到数据序列化后的长度紧接着将写入到指定位置处最后再将消息头字节数组写入到中整个编码过程就结束了本节的最后我们再来看一下对象的字段序列化过程也就是方法的逻辑如下依次序列化序列化调用方法名将参数类型转换为字符串并进行序列化对运行时参数进行序列化序列化至此关于服务消费方发送请求的过程就分析完了接下来我们来看一下服务提供方是如何接收请求的服务提供方接收请求前面说过默认情况下使用作为底层的通信框架检测到有数据入站后首先会通过解码器对数据进行解码并将解码后的数据传递给下一个入站处理器的指定方法所以在进行后续的分析之前我们先来看一下数据解码过程请求解码这里直接分析请求数据的解码逻辑忽略中间过程如下创建消息头字节数组读取消息头数据调用重载方法进行后续解码工作检查魔数是否相等通过命令行发送的数据包不包含消息头所以这里调用的方法对数据包进行解码检测可读数据量是否少于消息头长度若小于则立即返回从消息头中获取消息体长度检测消息体长度是否超出限制超出则抛出异常检测可读的字节数是否小于实际的字节数继续进行解码工作上面方法通过检测消息头中的魔数是否与规定的魔数相等提前拦截掉非常规数据包比如通过命令行发出的数据包接着再对消息体长度以及可读字节数进行检测最后调用方法进行后续的解码工作中实现了方法但因其子类覆写了该方法所以在运行时中的方法会被调用下面我们来看一下该方法的代码获取消息头中的第三个字节并通过逻辑与运算得到序列化器编号获取调用编号通过逻辑与运算得到调用类型对响应结果进行解码得到对象这个非本节内容后面再分析创建对象通过逻辑与运算得到通信方式并设置到对象中通过位运算检测数据包是否为事件类型设置心跳事件到对象中对心跳包进行解码该方法已被标注为废弃对事件数据进行解码根据参数判断是否在线程上对消息体进行解码在当前线程也就是线程上进行后续的解码工作此工作完成后可将调用方法名以及调用参数解析出来仅创建对象但不在当前线程上执行解码逻辑设置到对象中若解码过程中出现异常则将字段设为并将异常对象设置到对象中如上对部分字段进行了解码并将解码得到的字段封装到中随后会调用的方法进行后续的解码工作此工作完成后可将调用方法名以及运行时调用参数解析出来下面我们来看一下的方法逻辑通过反序列化得到并保存到变量中通过反序列化得到并保存到变量中通过反序列化得到调用方法名通过反序列化得到参数类型字符串比如将解析为参数类型数组解析运行时参数设置参数类型数组通过反序列化得到原的内容将与当前对象中的集合进行融合对类型的参数进行处理设置参数列表上面的方法通过反序列化将诸如调用方法名参数列表等信息依次解析出来并设置到相应的字段中最终得到一个具有完整调用信息的对象到这里请求数据解码的过程就分析完了此时我们得到了一个对象这个对象会被传送到下一个入站处理器中我们继续往下看调用服务解码器将数据包解析成对象后的方法紧接着会收到这个对象并将这个对象继续向下传递这期间该对象会被依次传递给以及最后由将该对象封装到实现类对象中并将放入线程池中执行后续的调用逻辑整个调用栈如下由线程池执行后续的调用逻辑考虑到篇幅以及很多中间调用的逻辑并非十分重要所以这里就不对调用栈中的每个方法都进行分析了这里我们直接分析调用栈中的分析第一个和最后一个调用方法逻辑如下这里的类型为获取继续向下调用如上中的逻辑比较简单首先根据一些信息获取实例然后将实例以及对象向下传递下面再来看看的逻辑在详细分析代码之前我们先来了解一下中的线程派发模型线程派发模型将底层通信框架中接收请求的线程称为线程如果一些事件处理逻辑可以很快执行完比如只在内存打一个标记此时直接在线程上执行该段逻辑即可但如果事件的处理逻辑比较耗时比如该段逻辑会发起数据库查询或者请求此时我们就不应该让事件处理逻辑在线程上执行而是应该派发到线程池中去执行原因也很简单线程主要用于接收请求如果线程被占满将导致它不能接收新的请求就是线程派发器需要说明的是真实的职责创建具有线程派发能力的比如和等其本身并不具备线程派发能力支持种不同的线程派发策略下面通过一个表格列举一下策略用途所有消息都派发到线程池包括请求响应连接事件断开事件等所有消息都不派发到线程池全部在线程上直接执行只有请求和响应消息派发到线程池其它消息均在线程上执行只有请求消息派发到线程池不含响应其它消息均在线程上执行在线程上将连接断开事件放入队列有序逐个执行其它消息派发到线程池默认配置下使用派发策略即将所有的消息都派发到线程池中下面我们来分析一下的代码处理连接事件获取线程池将连接事件派发到线程池中处理处理断开事件处理请求和响应消息这里的变量类型可能是也可能是将请求和响应消息派发到线程池中处理如果通信方式为双向通信此时将错误信息封装到中并返回给服务消费方返回包含错误信息的对象处理异常信息如上请求对象会被封装中将会是服务调用过程的新起点所以接下来我们以为起点向下探索调用服务本小节我们从开始分析该类的主要代码如下检测通道状态对于请求或响应消息此时将和传给对象进行后续的调用其他消息类型通过进行处理如上请求和响应消息出现频率明显比其他类型消息高所以这里对该类型的消息进行了针对性判断仅是一个中转站它的方法中并不包含具体的调用逻辑仅用于将参数传给其他对象进行处理该对象类型为对接口实现类对象进行解码对的字段进行解码对的字段进行解码执行后续逻辑接口目前有两个实现类分别为和执行解码逻辑主要是包含了一些解码逻辑节分析请求解码时说过请求解码可在线程上执行也可在线程池中执行这个取决于运行时配置存在的意义就是保证请求或响应对象可在线程池中被解码解码完毕后完全解码后的对象会继续向后传递下一站是处理请求对象处理事件处理普通的请求双向通信向后调用服务并得到调用结果将调用结果返回给服务消费端如果是单向通信仅向后调用指定服务即可无需返回调用结果处理响应对象服务消费方会执行此处逻辑后面分析相关忽略检测请求是否合法不合法则返回状态码为的响应设置状态获取字段值也就是对象继续向下调用设置状态码设置调用结果若调用过程出现异常则设置表示服务端异常到这里我们看到了比较清晰的请求和响应逻辑对于双向通信首先向后进行调用得到调用结果然后将调用结果封装到对象中最后再将该对象返回给服务消费方如果请求不合法或者调用失败则将错误信息封装到对象中并返回给服务消费方接下来我们继续向后分析把剩余的调用过程分析完下面分析定义在类中的匿名类对象逻辑如下获取实例回调相关忽略通过调用具体的服务忽略其他方法忽略回调和本地存根相关逻辑计算格式为比如从查找与相对应的对象服务导出过程中会将映射关系存储到集合中获取对象并返回忽略其他方法以上逻辑用于获取与指定服务对应的实例并通过的方法调用服务逻辑方法定义在中代码如下调用执行后续的调用并将调用结果封装到中并如上是一个抽象方法这个需要由具体的实例实现实例是在运行时通过创建的创建逻辑如下省略其他方法创建匿名类对象调用方法进行后续的调用是一个抽象类其中是一个抽象方法会在运行时通过框架为生成实现类并实现方法该方法最终会根据调用信息调用具体的服务以为例为其生成的代理类如下是在运行时生成的大家可使用进行反编译省略其他方法类型转换根据方法名调用指定的方法到这里整个服务调用过程就分析完了最后把调用过程贴出来如下服务提供方响应请求服务提供方调用指定服务后会将调用结果封装到对象中并将该对象返回给服务消费方服务提供方也是通过的方法将对象返回这个方法在节分析过这里就不在重复分析了本节我们仅需关注对象的编码过程即可这里仍然省略一些中间调用直接分析具体的编码逻辑对响应对象进行编码创建消息头字节数组设置魔数设置序列化器编号获取响应状态设置响应状态设置请求编号更新为消息头预留个字节的空间对心跳响应结果进行序列化已废弃对调用结果进行序列化对错误信息进行序列化获取写入的字节数也就是消息体长度将消息体长度写入到消息头中将指针移动到为写消息头做准备从下标处写入消息头设置新的原写下标消息头长度消息体长度异常处理逻辑不是很难理解但是代码略多这里忽略了检测当前协议版本是否支持带有集合的对象异常信息为空调用结果为空序列化响应类型调用结果非空序列化响应类型序列化调用结果异常信息非空序列化响应类型序列化异常对象记录协议版本序列化集合以上就是对象编码的过程和前面分析的对象编码过程很相似如果大家能看对象的编码逻辑那么这里的对象的编码逻辑也不难理解就不多说了接下来我们再来分析双向通信的最后一环服务消费方接收调用结果服务消费方接收调用结果服务消费方在收到响应数据后首先要做的事情是对响应数据进行解码得到对象然后再将该对象传给下一个入站处理器这个入站处理器就是接下来会将这个对象继续向下传递最后的方法会收到这个对象并将这个对象派发到线程池中这个过程和服务提供方接收请求的过程是一样的因此这里就不重复分析了本节我们重点分析两个方面的内容一是响应数据的解码过程二是如何将调用结果传递给用户线程的下面先来分析响应数据的解码过程响应数据解码响应数据解码逻辑主要的逻辑封装在中我们直接分析这个类的代码如下获取请求编号检测消息类型若下面的条件成立表明消息类型为创建对象检测事件标志位设置心跳事件获取响应状态设置响应状态如果响应状态为表明调用过程正常反序列化心跳数据已废弃反序列化事件数据根据参数决定是否在线程上执行解码逻辑创建对象进行后续的解码工作创建对象设置对象到对象中解码过程中出现了错误此时设置状态码到对象中响应状态非表明调用过程出现了异常反序列化异常信息并设置到对象中对请求数据进行解码前面已分析过此处忽略以上就是响应数据的解码过程上面逻辑看起来是不是似曾相识对的我们在前面章节分析过的方法中关于请求数据的解码过程该过程和响应数据的解码过程很相似下面我们继续分析调用结果的反序列化过程如下执行反序列化操作反序列化失败设置状态到对象中设置异常信息反序列化响应类型返回值为空且携带了集合反序列化集合并存储起来返回值不为空且携带了集合获取返回值类型反序列化调用结果并保存起来反序列化集合并存储起来异常对象不为空且携带了集合反序列化异常对象设置异常对象反序列化集合并存储起来本篇文章所分析的源码版本为该版本下的支持集合所以上面仅对部分分支进行了注释其他分支的逻辑比被注释分支的逻辑更为简单这里就忽略了我们所使用的测试服务接口包含了一个具有返回值的方法所以正常调用下线程会进入分支中然后线程会从变量大家探索一下变量的由来中获取返回值类型接着对调用结果进行反序列化并将序列化后的结果存储起来最后对集合进行反序列化并存到指定字段中到此关于响应数据的解码过程就分析完了接下来我们再来探索一下响应对象的去向向用户线程传递调用结果响应数据解码完成后会将响应对象派发到线程池上要注意的是线程池中的线程并非用户的调用线程所以要想办法将响应对象从线程池线程传递到用户线程上我们在节分析过用户线程在发送完请求后的动作即调用的方法等待响应对象的到来当响应对象到来后用户线程会被唤醒并通过调用编号获取属于自己的响应对象下面我们就来看一下整个过程对应的代码处理请求前面已分析过省略处理响应相关忽略继续向下调用根据调用编号从集合中查找指定的对象继续向下调用保存响应对象唤醒用户线程以上逻辑是将响应对象保存到相应的实例中然后再唤醒用户线程随后用户线程即可从实例中获取到相应结果本篇文章在多个地方都强调过调用编号很重要但一直没有解释原因这里简单说明一下一般情况下服务消费方会并发调用多个服务每个用户线程发送请求后会调用不同对象的方法进行等待一段时间后服务消费方的线程池会收到多个响应对象这个时候要考虑一个问题如何将每个响应对象传递给相应的对象且不出错答案是通过调用编号被创建时会要求传入一个对象此时可从对象中获取调用编号并将调用编号对象映射关系存入到静态中即线程池中的线程在收到对象后会根据对象中的调用编号到集合中取出相应的对象然后再将对象设置到对象中最后再唤醒用户线程这样用户线程即可从对象中获取调用结果了总结本篇文章主要对中的几种服务调用方式以及从双向通信的角度对整个通信过程进行了详细的分析按照通信顺序通信过程包括服务消费方发送请求服务提供方接收请求服务提供方返回响应数据服务消费方接收响应数据等过程理解这些过程需要大家对网络编程尤其是有一定的了解限于篇幅原因本篇文章无法将服务调用的所有内容都一一进行分析对于本篇文章未讲到或未详细分析的内容比如服务降级过滤器链以及序列化等大家若感兴趣可自行进行分析并将分析整理成文分享给社区",isPost:!0,isHome:!1,isHighlightShrink:!0,isToc:!0,postUpdate:"2020-09-27 22:29:54",postMainColor:""}</script><noscript><style>#nav{opacity:1}.justified-gallery img{opacity:1}#post-meta time,#recent-posts time{display:inline!important}</style></noscript><script>(e=>{e.saveToLocal={set:(e,t,a)=>{var o;0!==a&&(o=Date.now(),localStorage.setItem(e,JSON.stringify({value:t,expiry:o+864e5*a})))},get:e=>{var t=localStorage.getItem(e);if(t){t=JSON.parse(t);if(!(Date.now()>t.expiry))return t.value;localStorage.removeItem(e)}}},e.getScript=(o,c={})=>new Promise((t,e)=>{const a=document.createElement("script");a.src=o,a.async=!0,a.onerror=e,a.onload=a.onreadystatechange=function(){var e=this.readyState;e&&"loaded"!==e&&"complete"!==e||(a.onload=a.onreadystatechange=null,t())},Object.keys(c).forEach(e=>{a.setAttribute(e,c[e])}),document.head.appendChild(a)}),e.getCSS=(o,c=!1)=>new Promise((t,e)=>{const a=document.createElement("link");a.rel="stylesheet",a.href=o,c&&(a.id=c),a.onerror=e,a.onload=a.onreadystatechange=function(){var e=this.readyState;e&&"loaded"!==e&&"complete"!==e||(a.onload=a.onreadystatechange=null,t())},document.head.appendChild(a)}),e.activateDarkMode=()=>{document.documentElement.setAttribute("data-theme","dark"),null!==document.querySelector('meta[name="theme-color"]')&&document.querySelector('meta[name="theme-color"]').setAttribute("content","#0d0d0d")},e.activateLightMode=()=>{document.documentElement.setAttribute("data-theme","light"),null!==document.querySelector('meta[name="theme-color"]')&&document.querySelector('meta[name="theme-color"]').setAttribute("content","#ffffff")};var e=saveToLocal.get("theme"),t=window.matchMedia("(prefers-color-scheme: dark)").matches,a=window.matchMedia("(prefers-color-scheme: light)").matches,o=window.matchMedia("(prefers-color-scheme: no-preference)").matches,c=!t&&!a&&!o,t=(void 0===e?(a?activateLightMode():t?activateDarkMode():(o||c)&&((a=(new Date).getHours())<=6||18<=a?activateDarkMode:activateLightMode)(),window.matchMedia("(prefers-color-scheme: dark)").addListener(e=>{void 0===saveToLocal.get("theme")&&(e.matches?activateDarkMode:activateLightMode)()})):("light"===e?activateLightMode:activateDarkMode)(),saveToLocal.get("aside-status"));void 0!==t&&("hide"===t?document.documentElement.classList.add("hide-aside"):document.documentElement.classList.remove("hide-aside"));/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)&&document.documentElement.classList.add("apple")})(window)</script><link rel="stylesheet" href="/css/1.min.css?1" media="async" onload='this.media="all"'><link rel="stylesheet" href="/css/bg.css?1" media="async" onload='this.media="all"'><meta name="generator" content="Hexo 6.3.0"><link rel="alternate" href="/atom.xml" title="云少IT" type="application/atom+xml"><link rel="alternate" href="/rss.xml" title="云少IT" type="application/rss+xml"></head><body data-type="anzhiyu"><div id="web_bg"></div><div id="an_music_bg"></div><link rel="stylesheet" href="https://cdn.cbd.int/anzhiyu-theme-static@1.1.10/progress_bar/progress_bar.css"><script async src="https://cdn.cbd.int/pace-js@1.2.4/pace.min.js" data-pace-options="{ &quot;restartOnRequestAfter&quot;:false,&quot;eventLag&quot;:false}"></script><div class="post" id="body-wrap"><header class="post-bg" id="page-header"><nav id="nav"><div id="nav-group"><span id="blog_name"><div class="back-home-button"><i class="anzhiyufont anzhiyu-icon-grip-vertical"></i><div class="back-menu-list-groups"><div class="back-menu-list-group"><div class="back-menu-list-title">网页</div><div class="back-menu-list"><a class="back-menu-item" target="_blank" rel="noopener external nofollow noreferrer" href="https://www.tryrun.top/" title="博客"><img class="back-menu-item-icon" src="/img/favicon.ico" alt="博客"><span class="back-menu-item-text">博客</span></a></div></div><div class="back-menu-list-group"><div class="back-menu-list-title">项目</div><div class="back-menu-list"><a class="back-menu-item" target="_blank" rel="noopener external nofollow noreferrer" href="https://www.tryrun.top" title="图床"><img class="back-menu-item-icon" src="https://www.tryrun.top/favicon.ico" alt="图床"><span class="back-menu-item-text">图床</span></a></div></div></div></div><a id="site-name" href="/" accesskey="h"><div class="title">云少IT</div><i class="anzhiyufont anzhiyu-icon-house-chimney"></i></a></span><div class="mask-name-container"><div id="name-container"><a id="page-name" href="javascript:anzhiyu.scrollToDest(0, 500)" rel="external nofollow noreferrer">PAGE_NAME</a></div></div><div id="menus"><div class="menus_items"><div class="menus_item"><a class="site-page" href="javascript:void(0);" rel="external nofollow noreferrer"><span>望四方</span></a><ul class="menus_item_child"><li><a class="site-page child faa-parent animated-hover" href="/archives/"><i class="anzhiyufont anzhiyu-icon-box-archive faa-tada" style="font-size:.9em"></i><span> 归名档</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/categories/"><i class="anzhiyufont anzhiyu-icon-shapes faa-tada" style="font-size:.9em"></i><span> 归四类</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/tags/"><i class="anzhiyufont anzhiyu-icon-tags faa-tada" style="font-size:.9em"></i><span> 书中签</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/charts/"><i class="fa-fw fas fa-chart-bar faa-tada"></i><span> 统计</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="javascript:void(0);" rel="external nofollow noreferrer"><span>友链</span></a><ul class="menus_item_child"><li><a class="site-page child faa-parent animated-hover" href="/link/"><i class="anzhiyufont anzhiyu-icon-link faa-tada" style="font-size:.9em"></i><span> 四方好友</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/fcircle/"><i class="anzhiyufont anzhiyu-icon-artstation faa-tada" style="font-size:.9em"></i><span> 朋友圈</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/comments/"><i class="anzhiyufont anzhiyu-icon-envelope faa-tada" style="font-size:.9em"></i><span> 留言板</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="javascript:void(0);" rel="external nofollow noreferrer"><span>我的</span></a><ul class="menus_item_child"><li><a class="site-page child faa-parent animated-hover" href="/bangumis/"><i class="anzhiyufont anzhiyu-icon-bilibili faa-tada" style="font-size:.9em"></i><span> 追番页</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/album/"><i class="anzhiyufont anzhiyu-icon-images faa-tada" style="font-size:.9em"></i><span> 刹那间</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/equipment/"><i class="fas fa-heart faa-tada"></i><span> 我的装备</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/collect/"><i class="fas fa-camcorder faa-tada"></i><span> 观影阁</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="javascript:void(0);" rel="external nofollow noreferrer"><span>关于</span></a><ul class="menus_item_child"><li><a class="site-page child faa-parent animated-hover" href="/about/"><i class="anzhiyufont anzhiyu-icon-paper-plane faa-tada" style="font-size:.9em"></i><span> 关于本人</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/essay/"><i class="anzhiyufont anzhiyu-icon-lightbulb faa-tada" style="font-size:.9em"></i><span> 闲言碎语</span></a></li><li><a class="site-page child faa-parent animated-hover" href="javascript:toRandomPost()" rel="external nofollow noreferrer"><i class="anzhiyufont anzhiyu-icon-shoe-prints1 faa-tada" style="font-size:.9em"></i><span> 随便逛逛</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/disclaimer/"><i class="fas fa-heart faa-tada"></i><span> 免责声明</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/love/"><i class="anzhiyufont anzhiyu-icon-heartbeat faa-tada" style="font-size:.9em"></i><span> 恋爱小屋</span></a></li></ul></div></div></div><div id="nav-right"><div class="nav-button only-home" id="travellings_button" title="随机前往一个开往项目网站"><a class="site-page" onclick="anzhiyu.totraveling()" title="随机前往一个开往项目网站" href="javascript:void(0);" rel="external nofollow" data-pjax-state="external"><i class="anzhiyufont anzhiyu-icon-train"></i></a></div><div class="nav-button" id="randomPost_button"><a class="site-page" onclick="toRandomPost()" title="随机前往一个文章" href="javascript:void(0);" rel="external nofollow noreferrer"><i class="anzhiyufont anzhiyu-icon-dice"></i></a></div><div class="nav-button" id="search-button"><a class="site-page social-icon search" href="javascript:void(0);" rel="external nofollow noreferrer" title="搜索🔍" accesskey="s"><i class="anzhiyufont anzhiyu-icon-magnifying-glass"></i><span> 搜索</span></a></div><input id="center-console" type="checkbox"><label class="widget" for="center-console" title="中控台" onclick="anzhiyu.switchConsole()"><i class="left"></i><i class="widget center"></i><i class="widget right"></i></label><div id="console"><div class="console-card-group-reward"><ul class="reward-all console-card"><li class="reward-item"><a href="/img/wxpay.webp" target="_blank"><img class="post-qr-code-img" alt="wechat" src="/img/wxpay.webp"></a><div class="post-qr-code-desc">wechat</div></li><li class="reward-item"><a href="/img/alipay.webp" target="_blank"><img class="post-qr-code-img" alt="alipay" src="/img/alipay.webp"></a><div class="post-qr-code-desc">alipay</div></li></ul></div><div class="console-card-group"><div class="console-card-group-left"><div class="console-card" id="card-newest-comments"><div class="card-content"><div class="author-content-item-tips">互动</div><span class="author-content-item-title">最新评论</span></div><div class="aside-list"><span>正在加载中...</span></div></div></div><div class="console-card-group-right"><div class="console-card tags"><div class="card-content"><div class="author-content-item-tips">兴趣点</div><span class="author-content-item-title">寻找你感兴趣的领域</span><div class="card-tags"><div class="item-headline"></div><div class="card-tag-cloud"><a href="/tags/API/" style="font-size:1.05rem;color:#637571">API<sup>43</sup></a><a href="/tags/Base64/" style="font-size:1.05rem;color:#8a0460">Base64<sup>1</sup></a><a href="/tags/Collectors/" style="font-size:1.05rem;color:#6b3641">Collectors<sup>3</sup></a><a href="/tags/Date/" style="font-size:1.05rem;color:#5e6603">Date<sup>3</sup></a><a href="/tags/Executor/" style="font-size:1.05rem;color:#abb61f">Executor<sup>9</sup></a><a href="/tags/Guava/" style="font-size:1.05rem;color:#6dc55c">Guava<sup>1</sup></a><a href="/tags/JVM/" style="font-size:1.05rem;color:#8745c2">JVM<sup>8</sup></a><a href="/tags/Java8/" style="font-size:1.05rem;color:#804042">Java8<sup>21</sup></a><a href="/tags/Java9/" style="font-size:1.05rem;color:#21b4c6">Java9<sup>21</sup></a><a href="/tags/Java%E5%B9%B6%E5%8F%91/" style="font-size:1.05rem;color:#0a3988">Java并发<sup>20</sup></a><a href="/tags/Lambda/" style="font-size:1.05rem;color:#29446d">Lambda<sup>4</sup></a><a href="/tags/Lock/" style="font-size:1.05rem;color:#afc378">Lock<sup>1</sup></a><a href="/tags/Maven/" style="font-size:1.05rem;color:#b10843">Maven<sup>1</sup></a><a href="/tags/Memcached/" style="font-size:1.05rem;color:#9663a4">Memcached<sup>23</sup></a><a href="/tags/Mongodb/" style="font-size:1.05rem;color:#b4214d">Mongodb<sup>49</sup></a><a href="/tags/Queue/" style="font-size:1.05rem;color:#220f01">Queue<sup>1</sup></a><a href="/tags/Redis/" style="font-size:1.05rem;color:#c4106c">Redis<sup>27</sup></a><a href="/tags/Stream/" style="font-size:1.05rem;color:#1f5f9f">Stream<sup>4</sup></a><a href="/tags/Thread/" style="font-size:1.05rem;color:#c3563b">Thread<sup>7</sup></a><a href="/tags/Thread-pool/" style="font-size:1.05rem;color:#1a3364">Thread pool<sup>7</sup></a><a href="/tags/forkJoinPool/" style="font-size:1.05rem;color:#1ea132">forkJoinPool<sup>2</sup></a><a href="/tags/stream/" style="font-size:1.05rem;color:#a00679">stream<sup>1</sup></a><a href="/tags/%E4%B8%93%E6%A0%8F/" style="font-size:1.05rem;color:#9e5e9b">专栏<sup>35</sup></a><a href="/tags/%E4%BA%8B%E5%8A%A1/" style="font-size:1.05rem;color:#4d47bc">事务<sup>1</sup></a><a href="/tags/%E4%BC%98%E5%8C%96/" style="font-size:1.05rem;color:#a56245">优化<sup>1</sup></a><a href="/tags/%E5%91%BD%E4%BB%A4/" style="font-size:1.05rem;color:#9c8435">命令<sup>57</sup></a><a href="/tags/%E5%AE%89%E8%A3%85/" style="font-size:1.05rem;color:#647e28">安装<sup>6</sup></a><a href="/tags/%E5%B7%A5%E5%85%B7/" style="font-size:1.05rem;color:#a192b9">工具<sup>2</sup></a><a href="/tags/%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B/" style="font-size:1.05rem;color:#22a370">数据类型<sup>8</sup></a><a href="/tags/%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/" style="font-size:1.05rem;color:#6b985c">生命周期<sup>1</sup></a><a href="/tags/%E7%AE%80%E4%BB%8B/" style="font-size:1.05rem;color:#bc5f40">简介<sup>7</sup></a><a href="/tags/%E7%AE%97%E6%B3%95/" style="font-size:1.05rem;color:#167366">算法<sup>10</sup></a><a href="/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" style="font-size:1.05rem;color:#aa928f">设计模式<sup>38</sup></a><a href="/tags/%E9%85%8D%E7%BD%AE/" style="font-size:1.05rem;color:#3a740b">配置<sup>2</sup></a><a href="/tags/%E9%9D%A2%E8%AF%95/" style="font-size:1.05rem;color:#75c343">面试<sup>11</sup></a></div></div><hr></div></div><div class="console-card history"><div class="item-headline"><i class="anzhiyufont anzhiyu-icon-box-archiv"></i><span>文章</span></div><div class="card-archives"><div class="item-headline"><i class="anzhiyufont anzhiyu-icon-archive"></i><span>归档</span><a class="card-more-btn" href="/archives/" title="查看更多"> <i class="anzhiyufont anzhiyu-icon-angle-right"></i></a></div><ul class="card-archive-list"><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2023/06/"><span class="card-archive-list-date">六月 2023</span><div class="card-archive-list-count-group"><span class="card-archive-list-count">2</span><span>篇</span></div></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2021/05/"><span class="card-archive-list-date">五月 2021</span><div class="card-archive-list-count-group"><span class="card-archive-list-count">2</span><span>篇</span></div></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2020/10/"><span class="card-archive-list-date">十月 2020</span><div class="card-archive-list-count-group"><span class="card-archive-list-count">21</span><span>篇</span></div></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2020/09/"><span class="card-archive-list-date">九月 2020</span><div class="card-archive-list-count-group"><span class="card-archive-list-count">44</span><span>篇</span></div></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2020/08/"><span class="card-archive-list-date">八月 2020</span><div class="card-archive-list-count-group"><span class="card-archive-list-count">47</span><span>篇</span></div></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2020/07/"><span class="card-archive-list-date">七月 2020</span><div class="card-archive-list-count-group"><span class="card-archive-list-count">42</span><span>篇</span></div></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2020/06/"><span class="card-archive-list-date">六月 2020</span><div class="card-archive-list-count-group"><span class="card-archive-list-count">31</span><span>篇</span></div></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2020/05/"><span class="card-archive-list-date">五月 2020</span><div class="card-archive-list-count-group"><span class="card-archive-list-count">43</span><span>篇</span></div></a></li></ul></div><hr></div></div></div><div class="button-group"><div class="console-btn-item"><a class="darkmode_switchbutton" title="显示模式切换" href="javascript:void(0);" rel="external nofollow noreferrer"><i class="anzhiyufont anzhiyu-icon-moon"></i></a></div><div class="console-btn-item" id="consoleHideAside" onclick="anzhiyu.hideAsideBtn()" title="边栏显示控制"><a class="asideSwitch"><i class="anzhiyufont anzhiyu-icon-arrows-left-right"></i></a></div><div class="console-btn-item on" id="consoleCommentBarrage" onclick="anzhiyu.switchCommentBarrage()" title="热评开关"><a class="commentBarrage"><i class="anzhiyufont anzhiyu-icon-message"></i></a></div><div class="console-btn-item" id="consoleMusic" onclick="anzhiyu.musicToggle()" title="音乐开关"><a class="music-switch"><i class="anzhiyufont anzhiyu-icon-music"></i></a></div></div><div class="console-mask" onclick="anzhiyu.hideConsole()" href="javascript:void(0);" rel="external nofollow noreferrer"></div></div><div class="nav-button" id="nav-totop"><a class="totopbtn" href="javascript:void(0);" rel="external nofollow noreferrer"><i class="anzhiyufont anzhiyu-icon-arrow-up"></i><span id="percent" onclick="anzhiyu.scrollToDest(0,500)">0</span></a></div><div id="toggle-menu"><a class="site-page" href="javascript:void(0);" rel="external nofollow noreferrer" title="切换"><i class="anzhiyufont anzhiyu-icon-bars"></i></a></div></div></div></nav><div id="post-info"><div id="post-firstinfo"><div class="meta-firstline"><a class="post-meta-original">原创</a><span class="article-meta tags"></span></div></div><h1 class="post-title" itemprop="name headline">Dubbo 源码分析 – 服务调用过程</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="anzhiyufont anzhiyu-icon-calendar-days post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" itemprop="dateCreated datePublished" datetime="2020-09-27T14:29:54.000Z" title="发表于 2020-09-27 22:29:54">2020-09-27</time><span class="post-meta-separator"></span><i class="anzhiyufont anzhiyu-icon-history post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" itemprop="dateCreated datePublished" datetime="2020-09-27T14:29:54.000Z" title="更新于 2020-09-27 22:29:54">2020-09-27</time></span></div><div class="meta-secondline"><span class="post-meta-separator"></span><span class="post-meta-wordcount"><i class="anzhiyufont anzhiyu-icon-file-word post-meta-icon" title="文章字数"></i><span class="post-meta-label" title="文章字数">字数总计:</span><span class="word-count" title="文章字数">14.1k</span><span class="post-meta-separator"></span><i class="anzhiyufont anzhiyu-icon-clock post-meta-icon" title="阅读时长"></i><span class="post-meta-label" title="阅读时长">阅读时长:</span><span>59分钟</span></span><span class="post-meta-separator"></span><span class="post-meta-pv-cv" data-flag-title="Dubbo 源码分析 – 服务调用过程"><i class="anzhiyufont anzhiyu-icon-fw-eye post-meta-icon"></i><span class="post-meta-label" title="阅读量">阅读量:</span><span id="busuanzi_value_page_pv"><i class="anzhiyufont anzhiyu-icon-spinner anzhiyu-spin"></i></span></span><span class="post-meta-separator"> </span><span class="post-meta-position" title="作者IP属地为武汉"><i class="anzhiyufont anzhiyu-icon-location-dot"></i>武汉</span></div></div></div><section class="main-hero-waves-area waves-area"><svg class="waves-svg" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto"><defs><path id="gentle-wave" d="M -160 44 c 30 0 58 -18 88 -18 s 58 18 88 18 s 58 -18 88 -18 s 58 18 88 18 v 44 h -352 Z"></path></defs><g class="parallax"><use href="#gentle-wave" x="48" y="0"></use><use href="#gentle-wave" x="48" y="3"></use><use href="#gentle-wave" x="48" y="5"></use><use href="#gentle-wave" x="48" y="7"></use></g></svg></section><div id="post-top-cover"><img class="nolazyload" id="post-top-bg" src="https://www.bing.com/th?id=OHR.GlassOctopus_EN-GB6500292444_UHD.jpg"></div></header><main id="blog-container"><div class="layout" id="content-inner"><div id="post"><div class="post-ai-description"><div class="ai-title"><i class="anzhiyufont anzhiyu-icon-bilibili"></i><div class="ai-title-text">AI-摘要</div><i class="anzhiyufont anzhiyu-icon-arrow-rotate-right"></i><i class="anzhiyufont anzhiyu-icon-circle-dot" title="朗读摘要"></i><div id="ai-tag">Tianli GPT</div></div><div class="ai-explanation">AI初始化中...</div><div class="ai-btn-box"><div class="ai-btn-item">介绍自己 🙈</div><div class="ai-btn-item">生成本文简介 👋</div><div class="ai-btn-item">推荐相关文章 📖</div><div class="ai-btn-item">前往主页 🏠</div><div class="ai-btn-item" id="go-tianli-blog">前往爱发电购买</div></div><script data-pjax src="/js/anzhiyu/ai_abstract.js"></script></div><article class="post-content" id="article-container" itemscope itemtype="https://it985.github.io/posts/2ad0f5ed.html"><header><h1 id="CrawlerTitle" itemprop="name headline">Dubbo 源码分析 – 服务调用过程</h1><span itemprop="author" itemscope itemtype="http://schema.org/Person">云少</span><time itemprop="dateCreated datePublished" datetime="2020-09-27T14:29:54.000Z" title="发表于 2020-09-27 22:29:54">2020-09-27</time><time itemprop="dateCreated datePublished" datetime="2020-09-27T14:29:54.000Z" title="更新于 2020-09-27 22:29:54">2020-09-27</time></header><h1 id="九、Dubbo-源码分析-–-服务调用过程"><a href="#九、Dubbo-源码分析-–-服务调用过程" class="headerlink" title="九、Dubbo 源码分析 – 服务调用过程"></a>九、Dubbo 源码分析 – 服务调用过程</h1><h2 id="1-简介"><a href="#1-简介" class="headerlink" title="1. 简介"></a>1. 简介</h2><p>在前面的文章中，我们分析了 Dubbo SPI、服务导出与引入、以及集群容错方面的代码。经过前文的铺垫，本篇文章我们终于可以分析服务调用过程了。Dubbo 服务调用过程比较复杂，包含众多步骤。比如发送请求、编解码、服务降级、过滤器链处理、序列化、线程派发以及响应请求等步骤。限于篇幅原因，本篇文章无法对所有的步骤一一进行分析。本篇文章将会重点分析请求的发送与接收、编解码、线程派发以及响应的发送与接收等过程，至于服务降级、过滤器链和序列化大家自行进行分析，也可以将其当成一个黑盒，暂时忽略也没关系。介绍完本篇文章要分析的内容，接下来我们进入正题吧。</p><h2 id="2-源码分析"><a href="#2-源码分析" class="headerlink" title="2. 源码分析"></a>2. 源码分析</h2><p>在进行源码分析之前，我们先来了解 Dubbo 服务调用过程。</p><p>首先服务消费者通过代理对象 Proxy 发起远程调用，接着通过网络客户端 Client 将编码后的请求发送给服务提供方的网络层上，也就是 Server。Server 在收到请求后，首先要做的事情是对数据包进行解码。然后将解码后的请求发送至分发器 Dispatcher，再由分发器将请求派发到指定的线程池上，最后由线程池调用具体的服务。这就是一个远程调用请求的发送与接收过程。至于响应的发送与接收过程，这张图中没有表现出来。对于这两个过程，我们也会进行详细分析。</p><h3 id="2-1-服务调用方式"><a href="#2-1-服务调用方式" class="headerlink" title="2.1 服务调用方式"></a>2.1 服务调用方式</h3><p>Dubbo 支持同步和异步两种调用方式，其中异步调用还可细分为“有返回值”的异步调用和“无返回值”的异步调用。所谓“无返回值”异步调用是指服务消费方只管调用，但不关心调用结果，此时 Dubbo 会直接返回一个空的 RpcResult。若要使用异步特性，需要服务消费方手动进行配置。默认情况下，Dubbo 使用同步调用方式。</p><p>本节以及其他章节将会使用 Dubbo 官方提供的 Demo 分析整个调用过程，下面我们从 DemoService 接口的代理类开始进行分析。Dubbo 默认使用 Javassist 框架为服务接口生成动态代理类，因此我们需要先将代理类进行反编译才能看到源码。这里使用阿里开源 Java 应用诊断工具 <a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/alibaba/arthas">Arthas</a> 反编译代理类，结果如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Arthas 反编译步骤：</span></span><br><span class="line"><span class="comment"> * 1. 启动 Arthas</span></span><br><span class="line"><span class="comment"> *    java -jar arthas-boot.jar</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 2. 输入编号选择进程</span></span><br><span class="line"><span class="comment"> *    Arthas 启动后，会打印 Java 应用进程列表，比如：</span></span><br><span class="line"><span class="comment"> *    [1]: 11232 org.jetbrains.jps.cmdline.Launcher</span></span><br><span class="line"><span class="comment"> *    [2]: 22370 org.jetbrains.jps.cmdline.Launcher</span></span><br><span class="line"><span class="comment"> *    [3]: 22371 com.alibaba.dubbo.demo.consumer.Consumer</span></span><br><span class="line"><span class="comment"> *    [4]: 22362 com.alibaba.dubbo.demo.provider.Provider</span></span><br><span class="line"><span class="comment"> *    [5]: 2074 org.apache.zookeeper.server.quorum.QuorumPeerMain</span></span><br><span class="line"><span class="comment"> * 这里输入编号 3，让 Arthas 关联到启动类为 com.....Consumer 的 Java 进程上</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 3. 由于 Demo 项目中只有一个服务接口，因此此接口的代理类类名为 proxy0，此时使用 sc 命令搜索这个类名。</span></span><br><span class="line"><span class="comment"> *    $ sc *.proxy0</span></span><br><span class="line"><span class="comment"> *    com.alibaba.dubbo.common.bytecode.proxy0</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 4. 使用 jad 命令反编译 com.alibaba.dubbo.common.bytecode.proxy0</span></span><br><span class="line"><span class="comment"> *    $ jad com.alibaba.dubbo.common.bytecode.proxy0</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 更多使用方法请参考 Arthas 官方文档：</span></span><br><span class="line"><span class="comment"> *   https://alibaba.github.io/arthas/quick-start.html</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">proxy0</span> <span class="keyword">implements</span> <span class="title class_">ClassGenerator</span>.DC, EchoService, DemoService &#123;</span><br><span class="line">    <span class="comment">// 方法数组</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Method[] methods;</span><br><span class="line">    <span class="keyword">private</span> InvocationHandler handler;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">proxy0</span><span class="params">(InvocationHandler invocationHandler)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.handler = invocationHandler;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">proxy0</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">sayHello</span><span class="params">(String string)</span> &#123;</span><br><span class="line">        <span class="comment">// 将参数存储到 Object 数组中</span></span><br><span class="line">        Object[] arrobject = <span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;string&#125;;</span><br><span class="line">        <span class="comment">// 调用 InvocationHandler 实现类的 invoke 方法得到调用结果</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">object</span> <span class="operator">=</span> <span class="built_in">this</span>.handler.invoke(<span class="built_in">this</span>, methods[<span class="number">0</span>], arrobject);</span><br><span class="line">        <span class="comment">// 返回调用结果</span></span><br><span class="line">        <span class="keyword">return</span> (String)object;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 回声测试方法 */</span></span><br><span class="line">    <span class="keyword">public</span> Object $echo(Object object) &#123;</span><br><span class="line">        Object[] arrobject = <span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;object&#125;;</span><br><span class="line">        <span class="type">Object</span> <span class="variable">object2</span> <span class="operator">=</span> <span class="built_in">this</span>.handler.invoke(<span class="built_in">this</span>, methods[<span class="number">1</span>], arrobject);</span><br><span class="line">        <span class="keyword">return</span> object2;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如上，代理类的逻辑比较简单。首先将运行时参数存储到数组中，然后调用 InvocationHandler 接口实现类的 invoke 方法，得到调用结果，最后将结果转型并返回给调用方。关于代理类的逻辑就说这么多，继续向下分析。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">InvokerInvocationHandler</span> <span class="keyword">implements</span> <span class="title class_">InvocationHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Invoker&lt;?&gt; invoker;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">InvokerInvocationHandler</span><span class="params">(Invoker&lt;?&gt; handler)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.invoker = handler;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">methodName</span> <span class="operator">=</span> method.getName();</span><br><span class="line">        Class&lt;?&gt;[] parameterTypes = method.getParameterTypes();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 拦截定义在 Object 类中的方法（未被子类重写），比如 wait/notify</span></span><br><span class="line">        <span class="keyword">if</span> (method.getDeclaringClass() == Object.class) &#123;</span><br><span class="line">            <span class="keyword">return</span> method.invoke(invoker, args);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 如果 toString、hashCode 和 equals 等方法被子类重写了，这里也直接调用</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="string">&quot;toString&quot;</span>.equals(methodName) &amp;&amp; parameterTypes.length == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> invoker.toString();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">&quot;hashCode&quot;</span>.equals(methodName) &amp;&amp; parameterTypes.length == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> invoker.hashCode();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">&quot;equals&quot;</span>.equals(methodName) &amp;&amp; parameterTypes.length == <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> invoker.equals(args[<span class="number">0</span>]);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 将 method 和 args 封装到 RpcInvocation 中，并执行后续的调用</span></span><br><span class="line">        <span class="keyword">return</span> invoker.invoke(<span class="keyword">new</span> <span class="title class_">RpcInvocation</span>(method, args)).recreate();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>InvokerInvocationHandler 中的 invoker 成员变量类型为 MockClusterInvoker，MockClusterInvoker 内部封装了服务降级逻辑。下面简单看一下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MockClusterInvoker</span>&lt;T&gt; <span class="keyword">implements</span> <span class="title class_">Invoker</span>&lt;T&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Invoker&lt;T&gt; invoker;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">invoke</span><span class="params">(Invocation invocation)</span> <span class="keyword">throws</span> RpcException &#123;</span><br><span class="line">        <span class="type">Result</span> <span class="variable">result</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取 mock 配置值</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">value</span> <span class="operator">=</span> directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();</span><br><span class="line">        <span class="keyword">if</span> (value.length() == <span class="number">0</span> || value.equalsIgnoreCase(<span class="string">&quot;false&quot;</span>)) &#123;</span><br><span class="line">            <span class="comment">// 无 mock 逻辑，直接调用其他 Invoker 对象的 invoke 方法，</span></span><br><span class="line">            <span class="comment">// 比如 FailoverClusterInvoker</span></span><br><span class="line">            result = <span class="built_in">this</span>.invoker.invoke(invocation);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (value.startsWith(<span class="string">&quot;force&quot;</span>)) &#123;</span><br><span class="line">            <span class="comment">// force:xxx 直接执行 mock 逻辑，不发起远程调用</span></span><br><span class="line">            result = doMockInvoke(invocation, <span class="literal">null</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// fail:xxx 表示消费方对调用服务失败后，再执行 mock 逻辑，不抛出异常</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// 调用其他 Invoker 对象的 invoke 方法</span></span><br><span class="line">                result = <span class="built_in">this</span>.invoker.invoke(invocation);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (RpcException e) &#123;</span><br><span class="line">                <span class="keyword">if</span> (e.isBiz()) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> e;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="comment">// 调用失败，执行 mock 逻辑</span></span><br><span class="line">                    result = doMockInvoke(invocation, e);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 省略其他方法</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>服务降级不是本文重点，因此这里就不分析 doMockInvoke 方法了。考虑到前文已经详细分析过 FailoverClusterInvoker，因此本节略过 FailoverClusterInvoker，直接分析 DubboInvoker。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">AbstractInvoker</span>&lt;T&gt; <span class="keyword">implements</span> <span class="title class_">Invoker</span>&lt;T&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">invoke</span><span class="params">(Invocation inv)</span> <span class="keyword">throws</span> RpcException &#123;</span><br><span class="line">        <span class="keyword">if</span> (destroyed.get()) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RpcException</span>(<span class="string">&quot;Rpc invoker for service ...&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">RpcInvocation</span> <span class="variable">invocation</span> <span class="operator">=</span> (RpcInvocation) inv;</span><br><span class="line">        <span class="comment">// 设置 Invoker</span></span><br><span class="line">        invocation.setInvoker(<span class="built_in">this</span>);</span><br><span class="line">        <span class="keyword">if</span> (attachment != <span class="literal">null</span> &amp;&amp; attachment.size() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">// 设置 attachment</span></span><br><span class="line">            invocation.addAttachmentsIfAbsent(attachment);</span><br><span class="line">        &#125;</span><br><span class="line">        Map&lt;String, String&gt; contextAttachments = RpcContext.getContext().getAttachments();</span><br><span class="line">        <span class="keyword">if</span> (contextAttachments != <span class="literal">null</span> &amp;&amp; contextAttachments.size() != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">// 添加 contextAttachments 到 RpcInvocation#attachment 变量中</span></span><br><span class="line">            invocation.addAttachments(contextAttachments);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, <span class="literal">false</span>)) &#123;</span><br><span class="line">            <span class="comment">// 设置异步信息到 RpcInvocation#attachment 中</span></span><br><span class="line">            invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());</span><br><span class="line">        &#125;</span><br><span class="line">        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 抽象方法，由子类实现</span></span><br><span class="line">            <span class="keyword">return</span> doInvoke(invocation);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InvocationTargetException e) &#123;</span><br><span class="line">            <span class="comment">// ...</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (RpcException e) &#123;</span><br><span class="line">            <span class="comment">// ...</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable e) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RpcResult</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">abstract</span> Result <span class="title function_">doInvoke</span><span class="params">(Invocation invocation)</span> <span class="keyword">throws</span> Throwable;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 省略其他方法</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的代码来自 AbstractInvoker 类，其中大部分代码用于添加信息到 RpcInvocation#attachment 变量中，添加完毕后，调用 doInvoke 执行后续的调用。doInvoke 是一个抽象方法，需要由子类实现，下面到 DubboInvoker 中看一下。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DubboInvoker</span>&lt;T&gt; <span class="keyword">extends</span> <span class="title class_">AbstractInvoker</span>&lt;T&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ExchangeClient[] clients;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> Result <span class="title function_">doInvoke</span><span class="params">(<span class="keyword">final</span> Invocation invocation)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">        <span class="type">RpcInvocation</span> <span class="variable">inv</span> <span class="operator">=</span> (RpcInvocation) invocation;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">String</span> <span class="variable">methodName</span> <span class="operator">=</span> RpcUtils.getMethodName(invocation);</span><br><span class="line">        <span class="comment">// 设置 path 和 version 到 attachment 中</span></span><br><span class="line">        inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());</span><br><span class="line">        inv.setAttachment(Constants.VERSION_KEY, version);</span><br><span class="line"></span><br><span class="line">        ExchangeClient currentClient;</span><br><span class="line">        <span class="keyword">if</span> (clients.length == <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="comment">// 从 clients 数组中获取 ExchangeClient</span></span><br><span class="line">            currentClient = clients[<span class="number">0</span>];</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            currentClient = clients[index.getAndIncrement() % clients.length];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 获取异步配置</span></span><br><span class="line">            <span class="type">boolean</span> <span class="variable">isAsync</span> <span class="operator">=</span> RpcUtils.isAsync(getUrl(), invocation);</span><br><span class="line">            <span class="comment">// isOneway 为 true，表示“单向”通信</span></span><br><span class="line">            <span class="type">boolean</span> <span class="variable">isOneway</span> <span class="operator">=</span> RpcUtils.isOneway(getUrl(), invocation);</span><br><span class="line">            <span class="type">int</span> <span class="variable">timeout</span> <span class="operator">=</span> getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 异步无返回值</span></span><br><span class="line">            <span class="keyword">if</span> (isOneway) &#123;</span><br><span class="line">                <span class="type">boolean</span> <span class="variable">isSent</span> <span class="operator">=</span> getUrl().getMethodParameter(methodName, Constants.SENT_KEY, <span class="literal">false</span>);</span><br><span class="line">                <span class="comment">// 发送请求</span></span><br><span class="line">                currentClient.send(inv, isSent);</span><br><span class="line">                <span class="comment">// 设置上下文中的 future 为 null</span></span><br><span class="line">                RpcContext.getContext().setFuture(<span class="literal">null</span>);</span><br><span class="line">                <span class="comment">// 返回一个空的 RpcResult</span></span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RpcResult</span>();</span><br><span class="line">            &#125; </span><br><span class="line"></span><br><span class="line">            <span class="comment">// 异步有返回值</span></span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (isAsync) &#123;</span><br><span class="line">                <span class="comment">// 发送请求，获得 ResponseFuture 实例</span></span><br><span class="line">                <span class="type">ResponseFuture</span> <span class="variable">future</span> <span class="operator">=</span> currentClient.request(inv, timeout);</span><br><span class="line">                <span class="comment">// 设置 future 到上下文中</span></span><br><span class="line">                RpcContext.getContext().setFuture(<span class="keyword">new</span> <span class="title class_">FutureAdapter</span>&lt;Object&gt;(future));</span><br><span class="line">                <span class="comment">// 暂时返回一个空结果</span></span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RpcResult</span>();</span><br><span class="line">            &#125; </span><br><span class="line"></span><br><span class="line">            <span class="comment">// 同步调用</span></span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                RpcContext.getContext().setFuture(<span class="literal">null</span>);</span><br><span class="line">                <span class="comment">// 发送请求，得到一个 ResponseFuture 实例，并调用该实例的 get 方法进行等待</span></span><br><span class="line">                <span class="keyword">return</span> (Result) currentClient.request(inv, timeout).get();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (TimeoutException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RpcException</span>(..., <span class="string">&quot;Invoke remote method timeout....&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (RemotingException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RpcException</span>(..., <span class="string">&quot;Failed to invoke remote method: ...&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 省略其他方法</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的代码包含了 Dubbo 对同步和异步调用的处理逻辑，搞懂了上面的代码，会对 Dubbo 的同步和异步调用方式有更深入的了解。Dubbo 实现同步和异步调用比较关键的一点就在于由谁调用 ResponseFuture 的 get 方法。同步调用模式下，由框架自身调用 ResponseFuture 的 get 方法。异步调用模式下，则由用户调用该方法。ResponseFuture 是一个接口，下面我们来看一下它的默认实现类 DefaultFuture 的源码。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DefaultFuture</span> <span class="keyword">implements</span> <span class="title class_">ResponseFuture</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Map&lt;Long, Channel&gt; CHANNELS = </span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">ConcurrentHashMap</span>&lt;Long, Channel&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Map&lt;Long, DefaultFuture&gt; FUTURES = </span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">ConcurrentHashMap</span>&lt;Long, DefaultFuture&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">long</span> id;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Channel channel;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Request request;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> timeout;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Lock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Condition</span> <span class="variable">done</span> <span class="operator">=</span> lock.newCondition();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> Response response;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">DefaultFuture</span><span class="params">(Channel channel, Request request, <span class="type">int</span> timeout)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.channel = channel;</span><br><span class="line">        <span class="built_in">this</span>.request = request;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取请求 id，这个 id 很重要，后面还会见到</span></span><br><span class="line">        <span class="built_in">this</span>.id = request.getId();</span><br><span class="line">        <span class="built_in">this</span>.timeout = timeout &gt; <span class="number">0</span> ? timeout : channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);</span><br><span class="line">        <span class="comment">// 存储 &lt;requestId, DefaultFuture&gt; 映射关系到 FUTURES 中</span></span><br><span class="line">        FUTURES.put(id, <span class="built_in">this</span>);</span><br><span class="line">        CHANNELS.put(id, channel);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">get</span><span class="params">()</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="keyword">return</span> get(timeout);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">get</span><span class="params">(<span class="type">int</span> timeout)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="keyword">if</span> (timeout &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">            timeout = Constants.DEFAULT_TIMEOUT;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (!isDone()) &#123;</span><br><span class="line">            <span class="type">long</span> <span class="variable">start</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">            lock.lock();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// 循环检测服务提供方是否成功返回了调用结果</span></span><br><span class="line">                <span class="keyword">while</span> (!isDone()) &#123;</span><br><span class="line">                    <span class="comment">// 如果调用结果尚未返回，这里等待一段时间</span></span><br><span class="line">                    done.await(timeout, TimeUnit.MILLISECONDS);</span><br><span class="line">                    <span class="comment">// 如果调用结果成功返回，或等待超时，此时跳出 while 循环，执行后续的逻辑</span></span><br><span class="line">                    <span class="keyword">if</span> (isDone() || System.currentTimeMillis() - start &gt; timeout) &#123;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                lock.unlock();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 如果调用结果仍未返回，则抛出超时异常</span></span><br><span class="line">            <span class="keyword">if</span> (!isDone()) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TimeoutException</span>(sent &gt; <span class="number">0</span>, channel, getTimeoutMessage(<span class="literal">false</span>));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 返回调用结果</span></span><br><span class="line">        <span class="keyword">return</span> returnFromResponse();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isDone</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 通过检测 response 字段为空与否，判断是否收到了调用结果</span></span><br><span class="line">        <span class="keyword">return</span> response != <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Object <span class="title function_">returnFromResponse</span><span class="params">()</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="type">Response</span> <span class="variable">res</span> <span class="operator">=</span> response;</span><br><span class="line">        <span class="keyword">if</span> (res == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(<span class="string">&quot;response cannot be null&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 如果调用结果的状态为 Response.OK，则表示调用过程正常，服务提供方成功返回了调用结果</span></span><br><span class="line">        <span class="keyword">if</span> (res.getStatus() == Response.OK) &#123;</span><br><span class="line">            <span class="keyword">return</span> res.getResult();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 抛出异常</span></span><br><span class="line">        <span class="keyword">if</span> (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TimeoutException</span>(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RemotingException</span>(channel, res.getErrorMessage());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 省略其他方法</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如上，当服务消费者还未接收到调用结果时，用户线程调用 get 方法会被阻塞住。同步调用模式下，框架获得 DefaultFuture 对象后，会立即调用 get 方法进行等待。而异步模式下则是将该对象封装到 FutureAdapter 实例中，并将 FutureAdapter 实例设置到 RpcContext 中，供用户使用。FutureAdapter 是一个适配器，用于将 Dubbo 中的 ResponseFuture 与 JDK 中的 Future 进行适配。这样当用户线程调用 Future 的 get 方法时，经过 FutureAdapter 适配，最终会调用 ResponseFuture 实现类对象的 get 方法，也就是 DefaultFuture 的 get 方法。</p><p>到这里关于 Dubbo 几种调用方式的代码逻辑就分析完了，下面来分析请求数据的发送与接收，以及响应数据的发送与接收过程。</p><h3 id="2-2-服务消费方发送请求"><a href="#2-2-服务消费方发送请求" class="headerlink" title="2.2 服务消费方发送请求"></a>2.2 服务消费方发送请求</h3><h4 id="2-2-1-发送请求"><a href="#2-2-1-发送请求" class="headerlink" title="2.2.1 发送请求"></a>2.2.1 发送请求</h4><p>本节我们来看一下同步调用模式下，服务消费方是如何发送调用请求的。</p><p>服务消费方发送请求过程的部分调用栈，略为复杂。可以看出，经过多次调用后，才将请求数据送至 Netty NioClientSocketChannel。这样做的原因是通过 Exchange 层为框架引入 Request 和 Response 语义，这一点会在接下来的源码分析过程中会看到。其他的就不多说了，下面开始进行分析。首先分析 ReferenceCountExchangeClient 的源码。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">ReferenceCountExchangeClient</span> <span class="keyword">implements</span> <span class="title class_">ExchangeClient</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> URL url;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">AtomicInteger</span> <span class="variable">referenceCount</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ReferenceCountExchangeClient</span><span class="params">(ExchangeClient client, ConcurrentMap&lt;String, LazyConnectExchangeClient&gt; ghostClientMap)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.client = client;</span><br><span class="line">        <span class="comment">// 引用计数自增</span></span><br><span class="line">        referenceCount.incrementAndGet();</span><br><span class="line">        <span class="built_in">this</span>.url = client.getUrl();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> ResponseFuture <span class="title function_">request</span><span class="params">(Object request)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="comment">// 直接调用被装饰对象的同签名方法</span></span><br><span class="line">        <span class="keyword">return</span> client.request(request);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> ResponseFuture <span class="title function_">request</span><span class="params">(Object request, <span class="type">int</span> timeout)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="comment">// 直接调用被装饰对象的同签名方法</span></span><br><span class="line">        <span class="keyword">return</span> client.request(request, timeout);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 引用计数自增，该方法由外部调用 */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">incrementAndGetCount</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// referenceCount 自增</span></span><br><span class="line">        referenceCount.incrementAndGet();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">close</span><span class="params">(<span class="type">int</span> timeout)</span> &#123;</span><br><span class="line">        <span class="comment">// referenceCount 自减</span></span><br><span class="line">        <span class="keyword">if</span> (referenceCount.decrementAndGet() &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (timeout == <span class="number">0</span>) &#123;</span><br><span class="line">                client.close();</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                client.close(timeout);</span><br><span class="line">            &#125;</span><br><span class="line">            client = replaceWithLazyClient();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 省略部分方法</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>ReferenceCountExchangeClient 内部定义了一个引用计数变量 referenceCount，每当该对象被引用一次 referenceCount 都会进行自增。每当 close 方法被调用时，referenceCount 进行自减。ReferenceCountExchangeClient 内部仅实现了一个引用计数的功能，其他方法并无复杂逻辑，均是直接调用被装饰对象的相关方法。所以这里就不多说了，继续向下分析，这次是 HeaderExchangeClient。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HeaderExchangeClient</span> <span class="keyword">implements</span> <span class="title class_">ExchangeClient</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">ScheduledThreadPoolExecutor</span> <span class="variable">scheduled</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ScheduledThreadPoolExecutor</span>(<span class="number">2</span>, <span class="keyword">new</span> <span class="title class_">NamedThreadFactory</span>(<span class="string">&quot;dubbo-remoting-client-heartbeat&quot;</span>, <span class="literal">true</span>));</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Client client;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ExchangeChannel channel;</span><br><span class="line">    <span class="keyword">private</span> ScheduledFuture&lt;?&gt; heartbeatTimer;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> heartbeat;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> heartbeatTimeout;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">HeaderExchangeClient</span><span class="params">(Client client, <span class="type">boolean</span> needHeartbeat)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (client == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;client == null&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">this</span>.client = client;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建 HeaderExchangeChannel 对象</span></span><br><span class="line">        <span class="built_in">this</span>.channel = <span class="keyword">new</span> <span class="title class_">HeaderExchangeChannel</span>(client);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 以下代码均与心跳检测逻辑有关</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">dubbo</span> <span class="operator">=</span> client.getUrl().getParameter(Constants.DUBBO_VERSION_KEY);</span><br><span class="line">        <span class="built_in">this</span>.heartbeat = client.getUrl().getParameter(Constants.HEARTBEAT_KEY, dubbo != <span class="literal">null</span> &amp;&amp; dubbo.startsWith(<span class="string">&quot;1.0.&quot;</span>) ? Constants.DEFAULT_HEARTBEAT : <span class="number">0</span>);</span><br><span class="line">        <span class="built_in">this</span>.heartbeatTimeout = client.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * <span class="number">3</span>);</span><br><span class="line">        <span class="keyword">if</span> (heartbeatTimeout &lt; heartbeat * <span class="number">2</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(<span class="string">&quot;heartbeatTimeout &lt; heartbeatInterval * 2&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (needHeartbeat) &#123;</span><br><span class="line">            <span class="comment">// 开启心跳检测定时器</span></span><br><span class="line">            startHeartbeatTimer();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> ResponseFuture <span class="title function_">request</span><span class="params">(Object request)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="comment">// 直接 HeaderExchangeChannel 对象的同签名方法</span></span><br><span class="line">        <span class="keyword">return</span> channel.request(request);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> ResponseFuture <span class="title function_">request</span><span class="params">(Object request, <span class="type">int</span> timeout)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="comment">// 直接 HeaderExchangeChannel 对象的同签名方法</span></span><br><span class="line">        <span class="keyword">return</span> channel.request(request, timeout);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">close</span><span class="params">()</span> &#123;</span><br><span class="line">        doClose();</span><br><span class="line">        channel.close();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">doClose</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 停止心跳检测定时器</span></span><br><span class="line">        stopHeartbeatTimer();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">startHeartbeatTimer</span><span class="params">()</span> &#123;</span><br><span class="line">        stopHeartbeatTimer();</span><br><span class="line">        <span class="keyword">if</span> (heartbeat &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            heartbeatTimer = scheduled.scheduleWithFixedDelay(</span><br><span class="line">                    <span class="keyword">new</span> <span class="title class_">HeartBeatTask</span>(<span class="keyword">new</span> <span class="title class_">HeartBeatTask</span>.ChannelProvider() &#123;</span><br><span class="line">                        <span class="meta">@Override</span></span><br><span class="line">                        <span class="keyword">public</span> Collection&lt;Channel&gt; <span class="title function_">getChannels</span><span class="params">()</span> &#123;</span><br><span class="line">                            <span class="keyword">return</span> Collections.&lt;Channel&gt;singletonList(HeaderExchangeClient.<span class="built_in">this</span>);</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;, heartbeat, heartbeatTimeout),</span><br><span class="line">                    heartbeat, heartbeat, TimeUnit.MILLISECONDS);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">stopHeartbeatTimer</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (heartbeatTimer != <span class="literal">null</span> &amp;&amp; !heartbeatTimer.isCancelled()) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                heartbeatTimer.cancel(<span class="literal">true</span>);</span><br><span class="line">                scheduled.purge();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Throwable e) &#123;</span><br><span class="line">                <span class="keyword">if</span> (logger.isWarnEnabled()) &#123;</span><br><span class="line">                    logger.warn(e.getMessage(), e);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        heartbeatTimer = <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 省略部分方法</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>HeaderExchangeClient 中很多方法只有一行代码，即调用 HeaderExchangeChannel 对象的同签名方法。那 HeaderExchangeClient 有什么用处呢？答案是封装了一些关于心跳检测的逻辑。心跳检测并非本文所关注的点，因此就不多说了，继续向下看。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">HeaderExchangeChannel</span> <span class="keyword">implements</span> <span class="title class_">ExchangeChannel</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Channel channel;</span><br><span class="line"></span><br><span class="line">    HeaderExchangeChannel(Channel channel) &#123;</span><br><span class="line">        <span class="keyword">if</span> (channel == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;channel == null&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 这里的 channel 指向的是 NettyClient</span></span><br><span class="line">        <span class="built_in">this</span>.channel = channel;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> ResponseFuture <span class="title function_">request</span><span class="params">(Object request)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="keyword">return</span> request(request, channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> ResponseFuture <span class="title function_">request</span><span class="params">(Object request, <span class="type">int</span> timeout)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="keyword">if</span> (closed) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RemotingException</span>(..., <span class="string">&quot;Failed to send request ...&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 创建 Request 对象</span></span><br><span class="line">        <span class="type">Request</span> <span class="variable">req</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Request</span>();</span><br><span class="line">        req.setVersion(Version.getProtocolVersion());</span><br><span class="line">        <span class="comment">// 设置双向通信标志为 true</span></span><br><span class="line">        req.setTwoWay(<span class="literal">true</span>);</span><br><span class="line">        <span class="comment">// 这里的 request 变量类型为 RpcInvocation</span></span><br><span class="line">        req.setData(request);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建 DefaultFuture 对象</span></span><br><span class="line">        <span class="type">DefaultFuture</span> <span class="variable">future</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultFuture</span>(channel, req, timeout);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 调用 NettyClient 的 send 方法发送请求</span></span><br><span class="line">            channel.send(req);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (RemotingException e) &#123;</span><br><span class="line">            future.cancel();</span><br><span class="line">            <span class="keyword">throw</span> e;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 返回 DefaultFuture 对象</span></span><br><span class="line">        <span class="keyword">return</span> future;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>到这里大家终于看到了 Request 语义了，上面的方法首先定义了一个 Request 对象，然后再将该对象传给 NettyClient 的 send 方法，进行后续的调用。需要说明的是，NettyClient 中并未实现 send 方法，该方法继承自父类 AbstractPeer，下面直接分析 AbstractPeer 的代码。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">AbstractPeer</span> <span class="keyword">implements</span> <span class="title class_">Endpoint</span>, ChannelHandler &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">send</span><span class="params">(Object message)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="comment">// 该方法由 AbstractClient 类实现</span></span><br><span class="line">        send(message, url.getParameter(Constants.SENT_KEY, <span class="literal">false</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 省略其他方法</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">AbstractClient</span> <span class="keyword">extends</span> <span class="title class_">AbstractEndpoint</span> <span class="keyword">implements</span> <span class="title class_">Client</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">send</span><span class="params">(Object message, <span class="type">boolean</span> sent)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="keyword">if</span> (send_reconnect &amp;&amp; !isConnected()) &#123;</span><br><span class="line">            connect();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取 Channel，getChannel 是一个抽象方法，具体由子类实现</span></span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> getChannel();</span><br><span class="line">        <span class="keyword">if</span> (channel == <span class="literal">null</span> || !channel.isConnected()) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RemotingException</span>(<span class="built_in">this</span>, <span class="string">&quot;message can not send ...&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 继续向下调用</span></span><br><span class="line">        channel.send(message, sent);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">abstract</span> Channel <span class="title function_">getChannel</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 省略其他方法</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>默认情况下，Dubbo 使用 Netty 作为底层的通信框架，因此下面我们到 NettyClient 类中看一下 getChannel 方法的实现逻辑。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyClient</span> <span class="keyword">extends</span> <span class="title class_">AbstractClient</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 这里的 Channel 全限定名称为 org.jboss.netty.channel.Channel</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> Channel channel;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> com.alibaba.dubbo.remoting.Channel <span class="title function_">getChannel</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">c</span> <span class="operator">=</span> channel;</span><br><span class="line">        <span class="keyword">if</span> (c == <span class="literal">null</span> || !c.isConnected())</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="comment">// 获取一个 NettyChannel 类型对象</span></span><br><span class="line">        <span class="keyword">return</span> NettyChannel.getOrAddChannel(c, getUrl(), <span class="built_in">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">NettyChannel</span> <span class="keyword">extends</span> <span class="title class_">AbstractChannel</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> ConcurrentMap&lt;org.jboss.netty.channel.Channel, NettyChannel&gt; channelMap = </span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">ConcurrentHashMap</span>&lt;org.jboss.netty.channel.Channel, NettyChannel&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> org.jboss.netty.channel.Channel channel;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 私有构造方法 */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">NettyChannel</span><span class="params">(org.jboss.netty.channel.Channel channel, URL url, ChannelHandler handler)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>(url, handler);</span><br><span class="line">        <span class="keyword">if</span> (channel == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;netty channel == null;&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">this</span>.channel = channel;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> NettyChannel <span class="title function_">getOrAddChannel</span><span class="params">(org.jboss.netty.channel.Channel ch, URL url, ChannelHandler handler)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (ch == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 尝试从集合中获取 NettyChannel 实例</span></span><br><span class="line">        <span class="type">NettyChannel</span> <span class="variable">ret</span> <span class="operator">=</span> channelMap.get(ch);</span><br><span class="line">        <span class="keyword">if</span> (ret == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 如果 ret = null，则创建一个新的 NettyChannel 实例</span></span><br><span class="line">            <span class="type">NettyChannel</span> <span class="variable">nc</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NettyChannel</span>(ch, url, handler);</span><br><span class="line">            <span class="keyword">if</span> (ch.isConnected()) &#123;</span><br><span class="line">                <span class="comment">// 将 &lt;Channel, NettyChannel&gt; 键值对存入 channelMap 集合中</span></span><br><span class="line">                ret = channelMap.putIfAbsent(ch, nc);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (ret == <span class="literal">null</span>) &#123;</span><br><span class="line">                ret = nc;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ret;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>获取到 NettyChannel 实例后，即可进行后续的调用。下面看一下 NettyChannel 的 send 方法。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">send</span><span class="params">(Object message, <span class="type">boolean</span> sent)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">    <span class="built_in">super</span>.send(message, sent);</span><br><span class="line"></span><br><span class="line">    <span class="type">boolean</span> <span class="variable">success</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">timeout</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 发送消息(包含请求和响应消息)</span></span><br><span class="line">        <span class="type">ChannelFuture</span> <span class="variable">future</span> <span class="operator">=</span> channel.write(message);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// sent 的值源于 &lt;dubbo:method sent=&quot;true/false&quot; /&gt; 中 sent 的配置值，有两种配置值：</span></span><br><span class="line">        <span class="comment">//   1. true: 等待消息发出，消息发送失败将抛出异常</span></span><br><span class="line">        <span class="comment">//   2. false: 不等待消息发出，将消息放入 IO 队列，即刻返回</span></span><br><span class="line">        <span class="comment">// 默认情况下 sent = false；</span></span><br><span class="line">        <span class="keyword">if</span> (sent) &#123;</span><br><span class="line">            timeout = getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);</span><br><span class="line">            <span class="comment">// 等待消息发出，若在规定时间没能发出，success 会被置为 false</span></span><br><span class="line">            success = future.await(timeout);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">Throwable</span> <span class="variable">cause</span> <span class="operator">=</span> future.getCause();</span><br><span class="line">        <span class="keyword">if</span> (cause != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> cause;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Throwable e) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RemotingException</span>(<span class="built_in">this</span>, <span class="string">&quot;Failed to send message ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 若 success 为 false，这里抛出异常</span></span><br><span class="line">    <span class="keyword">if</span> (!success) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RemotingException</span>(<span class="built_in">this</span>, <span class="string">&quot;Failed to send message ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>经历多次调用，到这里请求数据的发送过程就结束了，过程漫长。为了便于大家阅读代码，这里以 DemoService 为例，将 sayHello 方法的整个调用路径贴出来。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">proxy0#sayHello(String)</span><br><span class="line">  —&gt; InvokerInvocationHandler#invoke(Object, Method, Object[])</span><br><span class="line">    —&gt; MockClusterInvoker#invoke(Invocation)</span><br><span class="line">      —&gt; AbstractClusterInvoker#invoke(Invocation)</span><br><span class="line">        —&gt; FailoverClusterInvoker#doInvoke(Invocation, List&lt;Invoker&lt;T&gt;&gt;, LoadBalance)</span><br><span class="line">          —&gt; Filter#invoke(Invoker, Invocation)  // 包含多个 Filter 调用</span><br><span class="line">            —&gt; ListenerInvokerWrapper#invoke(Invocation) </span><br><span class="line">              —&gt; AbstractInvoker#invoke(Invocation) </span><br><span class="line">                —&gt; DubboInvoker#doInvoke(Invocation)</span><br><span class="line">                  —&gt; ReferenceCountExchangeClient#request(Object, int)</span><br><span class="line">                    —&gt; HeaderExchangeClient#request(Object, int)</span><br><span class="line">                      —&gt; HeaderExchangeChannel#request(Object, int)</span><br><span class="line">                        —&gt; AbstractPeer#send(Object)</span><br><span class="line">                          —&gt; AbstractClient#send(Object, boolean)</span><br><span class="line">                            —&gt; NettyChannel#send(Object, boolean)</span><br><span class="line">                              —&gt; NioClientSocketChannel#write(Object)</span><br></pre></td></tr></table></figure><p>在 Netty 中，出站数据在发出之前还需要进行编码操作，接下来我们来分析一下请求数据的编码逻辑。</p><h4 id="2-2-2-请求编码"><a href="#2-2-2-请求编码" class="headerlink" title="2.2.2 请求编码"></a>2.2.2 请求编码</h4><p>在分析请求编码逻辑之前，我们先来看一下 Dubbo 数据包结构。</p><p>Dubbo 数据包分为消息头和消息体，消息头用于存储一些元信息，比如魔数（Magic），数据包类型（Request&#x2F;Response），消息体长度（Data Length）等。消息体中用于存储具体的调用消息，比如方法名称，参数列表等。下面简单列举一下消息头的内容。</p><table><thead><tr><th align="center">偏移量(Bit)</th><th align="center">字段</th><th align="center">取值</th></tr></thead><tbody><tr><td align="center">0 ~ 7</td><td align="center">魔数高位</td><td align="center">0xda00</td></tr><tr><td align="center">8 ~ 15</td><td align="center">魔数低位</td><td align="center">0xbb</td></tr><tr><td align="center">16</td><td align="center">数据包类型</td><td align="center">0 – Response, 1 – Request</td></tr><tr><td align="center">17</td><td align="center">调用方式</td><td align="center">仅在第16位被设为1的情况下有效，0 – 单向调用，1 – 双向调用</td></tr><tr><td align="center">18</td><td align="center">事件标识</td><td align="center">0 – 当前数据包是请求或响应包，1 – 当前数据包是心跳包</td></tr><tr><td align="center">19 ~ 23</td><td align="center">序列化器编号</td><td align="center">2 – Hessian2Serialization 3 – JavaSerialization 4 – CompactedJavaSerialization 6 – FastJsonSerialization 7 – NativeJavaSerialization 8 – KryoSerialization 9 – FstSerialization</td></tr><tr><td align="center">24 ~ 31</td><td align="center">状态</td><td align="center">20 – OK 30 – CLIENT_TIMEOUT 31 – SERVER_TIMEOUT 40 – BAD_REQUEST 50 – BAD_RESPONSE …</td></tr><tr><td align="center">32 ~ 95</td><td align="center">请求编号</td><td align="center">共8字节，运行时生成</td></tr><tr><td align="center">96 ~ 127</td><td align="center">消息体长度</td><td align="center">运行时计算</td></tr></tbody></table><p>了解了 Dubbo 数据包格式，接下来我们就可以探索编码过程了。这次我们开门见山，直接分析编码逻辑所在类。如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ExchangeCodec</span> <span class="keyword">extends</span> <span class="title class_">TelnetCodec</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 消息头长度</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">HEADER_LENGTH</span> <span class="operator">=</span> <span class="number">16</span>;</span><br><span class="line">    <span class="comment">// 魔数内容</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">short</span> <span class="variable">MAGIC</span> <span class="operator">=</span> (<span class="type">short</span>) <span class="number">0xdabb</span>;</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">byte</span> <span class="variable">MAGIC_HIGH</span> <span class="operator">=</span> Bytes.short2bytes(MAGIC)[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">byte</span> <span class="variable">MAGIC_LOW</span> <span class="operator">=</span> Bytes.short2bytes(MAGIC)[<span class="number">1</span>];</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">byte</span> <span class="variable">FLAG_REQUEST</span> <span class="operator">=</span> (<span class="type">byte</span>) <span class="number">0x80</span>;</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">byte</span> <span class="variable">FLAG_TWOWAY</span> <span class="operator">=</span> (<span class="type">byte</span>) <span class="number">0x40</span>;</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">byte</span> <span class="variable">FLAG_EVENT</span> <span class="operator">=</span> (<span class="type">byte</span>) <span class="number">0x20</span>;</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">SERIALIZATION_MASK</span> <span class="operator">=</span> <span class="number">0x1f</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Logger</span> <span class="variable">logger</span> <span class="operator">=</span> LoggerFactory.getLogger(ExchangeCodec.class);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Short <span class="title function_">getMagicCode</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> MAGIC;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">encode</span><span class="params">(Channel channel, ChannelBuffer buffer, Object msg)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="keyword">if</span> (msg <span class="keyword">instanceof</span> Request) &#123;</span><br><span class="line">            <span class="comment">// 对 Request 对象进行编码</span></span><br><span class="line">            encodeRequest(channel, buffer, (Request) msg);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (msg <span class="keyword">instanceof</span> Response) &#123;</span><br><span class="line">            <span class="comment">// 对 Response 对象进行编码，后面分析</span></span><br><span class="line">            encodeResponse(channel, buffer, (Response) msg);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="built_in">super</span>.encode(channel, buffer, msg);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">encodeRequest</span><span class="params">(Channel channel, ChannelBuffer buffer, Request req)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">Serialization</span> <span class="variable">serialization</span> <span class="operator">=</span> getSerialization(channel);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建消息头字节数组，长度为 16</span></span><br><span class="line">        <span class="type">byte</span>[] header = <span class="keyword">new</span> <span class="title class_">byte</span>[HEADER_LENGTH];</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 设置魔数</span></span><br><span class="line">        Bytes.short2bytes(MAGIC, header);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 设置数据包类型（Request/Response）和序列化器编号</span></span><br><span class="line">        header[<span class="number">2</span>] = (<span class="type">byte</span>) (FLAG_REQUEST | serialization.getContentTypeId());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 设置通信方式(单向/双向)</span></span><br><span class="line">        <span class="keyword">if</span> (req.isTwoWay()) &#123;</span><br><span class="line">            header[<span class="number">2</span>] |= FLAG_TWOWAY;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 设置事件标识</span></span><br><span class="line">        <span class="keyword">if</span> (req.isEvent()) &#123;</span><br><span class="line">            header[<span class="number">2</span>] |= FLAG_EVENT;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 设置请求编号，8个字节，从第4个字节开始设置</span></span><br><span class="line">        Bytes.long2bytes(req.getId(), header, <span class="number">4</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取 buffer 当前的写位置</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">savedWriteIndex</span> <span class="operator">=</span> buffer.writerIndex();</span><br><span class="line">        <span class="comment">// 更新 writerIndex，为消息头预留 16 个字节的空间</span></span><br><span class="line">        buffer.writerIndex(savedWriteIndex + HEADER_LENGTH);</span><br><span class="line">        <span class="type">ChannelBufferOutputStream</span> <span class="variable">bos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ChannelBufferOutputStream</span>(buffer);</span><br><span class="line">        <span class="comment">// 创建序列化器，比如 Hessian2ObjectOutput</span></span><br><span class="line">        <span class="type">ObjectOutput</span> <span class="variable">out</span> <span class="operator">=</span> serialization.serialize(channel.getUrl(), bos);</span><br><span class="line">        <span class="keyword">if</span> (req.isEvent()) &#123;</span><br><span class="line">            <span class="comment">// 对事件数据进行序列化操作</span></span><br><span class="line">            encodeEventData(channel, out, req.getData());</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 对请求数据进行序列化操作</span></span><br><span class="line">            encodeRequestData(channel, out, req.getData(), req.getVersion());</span><br><span class="line">        &#125;</span><br><span class="line">        out.flushBuffer();</span><br><span class="line">        <span class="keyword">if</span> (out <span class="keyword">instanceof</span> Cleanable) &#123;</span><br><span class="line">            ((Cleanable) out).cleanup();</span><br><span class="line">        &#125;</span><br><span class="line">        bos.flush();</span><br><span class="line">        bos.close();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取写入的字节数，也就是消息体长度</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> bos.writtenBytes();</span><br><span class="line">        checkPayload(channel, len);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 将消息体长度写入到消息头中</span></span><br><span class="line">        Bytes.int2bytes(len, header, <span class="number">12</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 将 buffer 指针移动到 savedWriteIndex，为写消息头做准备</span></span><br><span class="line">        buffer.writerIndex(savedWriteIndex);</span><br><span class="line">        <span class="comment">// 从 savedWriteIndex 下标处写入消息头</span></span><br><span class="line">        buffer.writeBytes(header);</span><br><span class="line">        <span class="comment">// 设置新的 writerIndex，writerIndex = 原写下标 + 消息头长度 + 消息体长度</span></span><br><span class="line">        buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + len);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 省略其他方法</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上就是请求对象的编码过程，该过程首先会通过位运算将消息头写入到 header 数组中。然后对 Request 对象的 data 字段执行序列化操作，序列化后的数据最终会存储到 ChannelBuffer 中。序列化操作执行完后，可得到数据序列化后的长度 len，紧接着将 len 写入到 header 指定位置处。最后再将消息头字节数组 header 写入到 ChannelBuffer 中，整个编码过程就结束了。本节的最后，我们再来看一下 Request 对象的 data 字段序列化过程，也就是 encodeRequestData 方法的逻辑，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DubboCodec</span> <span class="keyword">extends</span> <span class="title class_">ExchangeCodec</span> <span class="keyword">implements</span> <span class="title class_">Codec2</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">encodeRequestData</span><span class="params">(Channel channel, ObjectOutput out, Object data, String version)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">RpcInvocation</span> <span class="variable">inv</span> <span class="operator">=</span> (RpcInvocation) data;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 依次序列化 dubbo version、path、version</span></span><br><span class="line">        out.writeUTF(version);</span><br><span class="line">        out.writeUTF(inv.getAttachment(Constants.PATH_KEY));</span><br><span class="line">        out.writeUTF(inv.getAttachment(Constants.VERSION_KEY));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 序列化调用方法名</span></span><br><span class="line">        out.writeUTF(inv.getMethodName());</span><br><span class="line">        <span class="comment">// 将参数类型转换为字符串，并进行序列化</span></span><br><span class="line">        out.writeUTF(ReflectUtils.getDesc(inv.getParameterTypes()));</span><br><span class="line">        Object[] args = inv.getArguments();</span><br><span class="line">        <span class="keyword">if</span> (args != <span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; args.length; i++) &#123;</span><br><span class="line">                <span class="comment">// 对运行时参数进行序列化</span></span><br><span class="line">                out.writeObject(encodeInvocationArgument(channel, inv, i));</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 序列化 attachments</span></span><br><span class="line">        out.writeObject(inv.getAttachments());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>至此，关于服务消费方发送请求的过程就分析完了，接下来我们来看一下服务提供方是如何接收请求的。</p><h3 id="2-3-服务提供方接收请求"><a href="#2-3-服务提供方接收请求" class="headerlink" title="2.3 服务提供方接收请求"></a>2.3 服务提供方接收请求</h3><p>前面说过，默认情况下 Dubbo 使用 Netty 作为底层的通信框架。Netty 检测到有数据入站后，首先会通过解码器对数据进行解码，并将解码后的数据传递给下一个入站处理器的指定方法。所以在进行后续的分析之前，我们先来看一下数据解码过程。</p><h4 id="2-3-1-请求解码"><a href="#2-3-1-请求解码" class="headerlink" title="2.3.1 请求解码"></a>2.3.1 请求解码</h4><p>这里直接分析请求数据的解码逻辑，忽略中间过程，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ExchangeCodec</span> <span class="keyword">extends</span> <span class="title class_">TelnetCodec</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">decode</span><span class="params">(Channel channel, ChannelBuffer buffer)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">readable</span> <span class="operator">=</span> buffer.readableBytes();</span><br><span class="line">        <span class="comment">// 创建消息头字节数组</span></span><br><span class="line">        <span class="type">byte</span>[] header = <span class="keyword">new</span> <span class="title class_">byte</span>[Math.min(readable, HEADER_LENGTH)];</span><br><span class="line">        <span class="comment">// 读取消息头数据</span></span><br><span class="line">        buffer.readBytes(header);</span><br><span class="line">        <span class="comment">// 调用重载方法进行后续解码工作</span></span><br><span class="line">        <span class="keyword">return</span> decode(channel, buffer, readable, header);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> Object <span class="title function_">decode</span><span class="params">(Channel channel, ChannelBuffer buffer, <span class="type">int</span> readable, <span class="type">byte</span>[] header)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="comment">// 检查魔数是否相等</span></span><br><span class="line">        <span class="keyword">if</span> (readable &gt; <span class="number">0</span> &amp;&amp; header[<span class="number">0</span>] != MAGIC_HIGH</span><br><span class="line">                || readable &gt; <span class="number">1</span> &amp;&amp; header[<span class="number">1</span>] != MAGIC_LOW) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">length</span> <span class="operator">=</span> header.length;</span><br><span class="line">            <span class="keyword">if</span> (header.length &lt; readable) &#123;</span><br><span class="line">                header = Bytes.copyOf(header, readable);</span><br><span class="line">                buffer.readBytes(header, length, readable - length);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt; header.length - <span class="number">1</span>; i++) &#123;</span><br><span class="line">                <span class="keyword">if</span> (header[i] == MAGIC_HIGH &amp;&amp; header[i + <span class="number">1</span>] == MAGIC_LOW) &#123;</span><br><span class="line">                    buffer.readerIndex(buffer.readerIndex() - header.length + i);</span><br><span class="line">                    header = Bytes.copyOf(header, i);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 通过 telnet 命令行发送的数据包不包含消息头，所以这里</span></span><br><span class="line">            <span class="comment">// 调用 TelnetCodec 的 decode 方法对数据包进行解码</span></span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">super</span>.decode(channel, buffer, readable, header);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 检测可读数据量是否少于消息头长度，若小于则立即返回 DecodeResult.NEED_MORE_INPUT</span></span><br><span class="line">        <span class="keyword">if</span> (readable &lt; HEADER_LENGTH) &#123;</span><br><span class="line">            <span class="keyword">return</span> DecodeResult.NEED_MORE_INPUT;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 从消息头中获取消息体长度</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> Bytes.bytes2int(header, <span class="number">12</span>);</span><br><span class="line">        <span class="comment">// 检测消息体长度是否超出限制，超出则抛出异常</span></span><br><span class="line">        checkPayload(channel, len);</span><br><span class="line"></span><br><span class="line">        <span class="type">int</span> <span class="variable">tt</span> <span class="operator">=</span> len + HEADER_LENGTH;</span><br><span class="line">        <span class="comment">// 检测可读的字节数是否小于实际的字节数</span></span><br><span class="line">        <span class="keyword">if</span> (readable &lt; tt) &#123;</span><br><span class="line">            <span class="keyword">return</span> DecodeResult.NEED_MORE_INPUT;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="type">ChannelBufferInputStream</span> <span class="variable">is</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ChannelBufferInputStream</span>(buffer, len);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 继续进行解码工作</span></span><br><span class="line">            <span class="keyword">return</span> decodeBody(channel, is, header);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (is.available() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    StreamUtils.skipUnusedStream(is);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                    logger.warn(e.getMessage(), e);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面方法通过检测消息头中的魔数是否与规定的魔数相等，提前拦截掉非常规数据包，比如通过 telnet 命令行发出的数据包。接着再对消息体长度，以及可读字节数进行检测。最后调用 decodeBody 方法进行后续的解码工作，ExchangeCodec 中实现了 decodeBody 方法，但因其子类 DubboCodec 覆写了该方法，所以在运行时 DubboCodec 中的 decodeBody 方法会被调用。下面我们来看一下该方法的代码。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DubboCodec</span> <span class="keyword">extends</span> <span class="title class_">ExchangeCodec</span> <span class="keyword">implements</span> <span class="title class_">Codec2</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> Object <span class="title function_">decodeBody</span><span class="params">(Channel channel, InputStream is, <span class="type">byte</span>[] header)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="comment">// 获取消息头中的第三个字节，并通过逻辑与运算得到序列化器编号</span></span><br><span class="line">        <span class="type">byte</span> <span class="variable">flag</span> <span class="operator">=</span> header[<span class="number">2</span>], proto = (<span class="type">byte</span>) (flag &amp; SERIALIZATION_MASK);</span><br><span class="line">        <span class="type">Serialization</span> <span class="variable">s</span> <span class="operator">=</span> CodecSupport.getSerialization(channel.getUrl(), proto);</span><br><span class="line">        <span class="comment">// 获取调用编号</span></span><br><span class="line">        <span class="type">long</span> <span class="variable">id</span> <span class="operator">=</span> Bytes.bytes2long(header, <span class="number">4</span>);</span><br><span class="line">        <span class="comment">// 通过逻辑与运算得到调用类型，0 - Response，1 - Request</span></span><br><span class="line">        <span class="keyword">if</span> ((flag &amp; FLAG_REQUEST) == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">// 对响应结果进行解码，得到 Response 对象。这个非本节内容，后面再分析</span></span><br><span class="line">            <span class="comment">// ...</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 创建 Request 对象</span></span><br><span class="line">            <span class="type">Request</span> <span class="variable">req</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Request</span>(id);</span><br><span class="line">            req.setVersion(Version.getProtocolVersion());</span><br><span class="line">            <span class="comment">// 通过逻辑与运算得到通信方式，并设置到 Request 对象中</span></span><br><span class="line">            req.setTwoWay((flag &amp; FLAG_TWOWAY) != <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 通过位运算检测数据包是否为事件类型</span></span><br><span class="line">            <span class="keyword">if</span> ((flag &amp; FLAG_EVENT) != <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="comment">// 设置心跳事件到 Request 对象中</span></span><br><span class="line">                req.setEvent(Request.HEARTBEAT_EVENT);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                Object data;</span><br><span class="line">                <span class="keyword">if</span> (req.isHeartbeat()) &#123;</span><br><span class="line">                    <span class="comment">// 对心跳包进行解码，该方法已被标注为废弃</span></span><br><span class="line">                    data = decodeHeartbeatData(channel, deserialize(s, channel.getUrl(), is));</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span> (req.isEvent()) &#123;</span><br><span class="line">                    <span class="comment">// 对事件数据进行解码</span></span><br><span class="line">                    data = decodeEventData(channel, deserialize(s, channel.getUrl(), is));</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    DecodeableRpcInvocation inv;</span><br><span class="line">                    <span class="comment">// 根据 url 参数判断是否在 IO 线程上对消息体进行解码</span></span><br><span class="line">                    <span class="keyword">if</span> (channel.getUrl().getParameter(</span><br><span class="line">                            Constants.DECODE_IN_IO_THREAD_KEY,</span><br><span class="line">                            Constants.DEFAULT_DECODE_IN_IO_THREAD)) &#123;</span><br><span class="line">                        inv = <span class="keyword">new</span> <span class="title class_">DecodeableRpcInvocation</span>(channel, req, is, proto);</span><br><span class="line">                        <span class="comment">// 在当前线程，也就是 IO 线程上进行后续的解码工作。此工作完成后，可将</span></span><br><span class="line">                        <span class="comment">// 调用方法名、attachment、以及调用参数解析出来</span></span><br><span class="line">                        inv.decode();</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        <span class="comment">// 仅创建 DecodeableRpcInvocation 对象，但不在当前线程上执行解码逻辑</span></span><br><span class="line">                        inv = <span class="keyword">new</span> <span class="title class_">DecodeableRpcInvocation</span>(channel, req,</span><br><span class="line">                                <span class="keyword">new</span> <span class="title class_">UnsafeByteArrayInputStream</span>(readMessageData(is)), proto);</span><br><span class="line">                    &#125;</span><br><span class="line">                    data = inv;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 设置 data 到 Request 对象中</span></span><br><span class="line">                req.setData(data);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Throwable t) &#123;</span><br><span class="line">                <span class="comment">// 若解码过程中出现异常，则将 broken 字段设为 true，</span></span><br><span class="line">                <span class="comment">// 并将异常对象设置到 Reqeust 对象中</span></span><br><span class="line">                req.setBroken(<span class="literal">true</span>);</span><br><span class="line">                req.setData(t);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> req;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如上，decodeBody 对部分字段进行了解码，并将解码得到的字段封装到 Request 中。随后会调用 DecodeableRpcInvocation 的 decode 方法进行后续的解码工作。此工作完成后，可将调用方法名、attachment、以及运行时调用参数解析出来。下面我们来看一下 DecodeableRpcInvocation 的 decode 方法逻辑。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DecodeableRpcInvocation</span> <span class="keyword">extends</span> <span class="title class_">RpcInvocation</span> <span class="keyword">implements</span> <span class="title class_">Codec</span>, Decodeable &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">decode</span><span class="params">(Channel channel, InputStream input)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">ObjectInput</span> <span class="variable">in</span> <span class="operator">=</span> CodecSupport.getSerialization(channel.getUrl(), serializationType)</span><br><span class="line">                .deserialize(channel.getUrl(), input);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 通过反序列化得到 dubbo version，并保存到 attachments 变量中</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">dubboVersion</span> <span class="operator">=</span> in.readUTF();</span><br><span class="line">        request.setVersion(dubboVersion);</span><br><span class="line">        setAttachment(Constants.DUBBO_VERSION_KEY, dubboVersion);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 通过反序列化得到 path，version，并保存到 attachments 变量中</span></span><br><span class="line">        setAttachment(Constants.PATH_KEY, in.readUTF());</span><br><span class="line">        setAttachment(Constants.VERSION_KEY, in.readUTF());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 通过反序列化得到调用方法名</span></span><br><span class="line">        setMethodName(in.readUTF());</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Object[] args;</span><br><span class="line">            Class&lt;?&gt;[] pts;</span><br><span class="line">            <span class="comment">// 通过反序列化得到参数类型字符串，比如 Ljava/lang/String;</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">desc</span> <span class="operator">=</span> in.readUTF();</span><br><span class="line">            <span class="keyword">if</span> (desc.length() == <span class="number">0</span>) &#123;</span><br><span class="line">                pts = DubboCodec.EMPTY_CLASS_ARRAY;</span><br><span class="line">                args = DubboCodec.EMPTY_OBJECT_ARRAY;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 将 desc 解析为参数类型数组</span></span><br><span class="line">                pts = ReflectUtils.desc2classArray(desc);</span><br><span class="line">                args = <span class="keyword">new</span> <span class="title class_">Object</span>[pts.length];</span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; args.length; i++) &#123;</span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        <span class="comment">// 解析运行时参数</span></span><br><span class="line">                        args[i] = in.readObject(pts[i]);</span><br><span class="line">                    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                        <span class="keyword">if</span> (log.isWarnEnabled()) &#123;</span><br><span class="line">                            log.warn(<span class="string">&quot;Decode argument failed: &quot;</span> + e.getMessage(), e);</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 设置参数类型数组</span></span><br><span class="line">            setParameterTypes(pts);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 通过反序列化得到原 attachments 的内容</span></span><br><span class="line">            Map&lt;String, String&gt; map = (Map&lt;String, String&gt;) in.readObject(Map.class);</span><br><span class="line">            <span class="keyword">if</span> (map != <span class="literal">null</span> &amp;&amp; map.size() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                Map&lt;String, String&gt; attachment = getAttachments();</span><br><span class="line">                <span class="keyword">if</span> (attachment == <span class="literal">null</span>) &#123;</span><br><span class="line">                    attachment = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;String, String&gt;();</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// 将 map 与当前对象中的 attachment 集合进行融合</span></span><br><span class="line">                attachment.putAll(map);</span><br><span class="line">                setAttachments(attachment);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 对 callback 类型的参数进行处理</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; args.length; i++) &#123;</span><br><span class="line">                args[i] = decodeInvocationArgument(channel, <span class="built_in">this</span>, pts, i, args[i]);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 设置参数列表</span></span><br><span class="line">            setArguments(args);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IOException</span>(StringUtils.toString(<span class="string">&quot;Read invocation data failed.&quot;</span>, e));</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (in <span class="keyword">instanceof</span> Cleanable) &#123;</span><br><span class="line">                ((Cleanable) in).cleanup();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的方法通过反序列化将诸如 path、version、调用方法名、参数列表等信息依次解析出来，并设置到相应的字段中，最终得到一个具有完整调用信息的 DecodeableRpcInvocation 对象。</p><p>到这里，请求数据解码的过程就分析完了。此时我们得到了一个 Request 对象，这个对象会被传送到下一个入站处理器中，我们继续往下看。</p><h4 id="2-3-2-调用服务"><a href="#2-3-2-调用服务" class="headerlink" title="2.3.2 调用服务"></a>2.3.2 调用服务</h4><p>解码器将数据包解析成 Request 对象后，NettyHandler 的 messageReceived 方法紧接着会收到这个对象，并将这个对象继续向下传递。这期间该对象会被依次传递给 NettyServer、MultiMessageHandler、HeartbeatHandler 以及 AllChannelHandler。最后由 AllChannelHandler 将该对象封装到 Runnable 实现类对象中，并将 Runnable 放入线程池中执行后续的调用逻辑。整个调用栈如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">NettyHandler#messageReceived(ChannelHandlerContext, MessageEvent)</span><br><span class="line">  —&gt; AbstractPeer#received(Channel, Object)</span><br><span class="line">    —&gt; MultiMessageHandler#received(Channel, Object)</span><br><span class="line">      —&gt; HeartbeatHandler#received(Channel, Object)</span><br><span class="line">        —&gt; AllChannelHandler#received(Channel, Object)</span><br><span class="line">          —&gt; ExecutorService#execute(Runnable)    // 由线程池执行后续的调用逻辑</span><br></pre></td></tr></table></figure><p>考虑到篇幅，以及很多中间调用的逻辑并非十分重要，所以这里就不对调用栈中的每个方法都进行分析了。这里我们直接分析调用栈中的分析第一个和最后一个调用方法逻辑。如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Sharable</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyHandler</span> <span class="keyword">extends</span> <span class="title class_">SimpleChannelHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Map&lt;String, Channel&gt; channels = <span class="keyword">new</span> <span class="title class_">ConcurrentHashMap</span>&lt;String, Channel&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> URL url;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ChannelHandler handler;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">NettyHandler</span><span class="params">(URL url, ChannelHandler handler)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (url == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;url == null&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (handler == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;handler == null&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">this</span>.url = url;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 这里的 handler 类型为 NettyServer</span></span><br><span class="line">        <span class="built_in">this</span>.handler = handler;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">messageReceived</span><span class="params">(ChannelHandlerContext ctx, MessageEvent e)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">// 获取 NettyChannel</span></span><br><span class="line">        <span class="type">NettyChannel</span> <span class="variable">channel</span> <span class="operator">=</span> NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 继续向下调用</span></span><br><span class="line">            handler.received(channel, e.getMessage());</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            NettyChannel.removeChannelIfDisconnected(ctx.getChannel());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如上，NettyHandler 中的 messageReceived 逻辑比较简单。首先根据一些信息获取 NettyChannel 实例，然后将 NettyChannel 实例以及 Request 对象向下传递。下面再来看看 AllChannelHandler 的逻辑，在详细分析代码之前，我们先来了解一下 Dubbo 中的线程派发模型。</p><h5 id="2-3-2-1-线程派发模型"><a href="#2-3-2-1-线程派发模型" class="headerlink" title="2.3.2.1 线程派发模型"></a>2.3.2.1 线程派发模型</h5><p>Dubbo 将底层通信框架中接收请求的线程称为 IO 线程。如果一些事件处理逻辑可以很快执行完，比如只在内存打一个标记，此时直接在 IO 线程上执行该段逻辑即可。但如果事件的处理逻辑比较耗时，比如该段逻辑会发起数据库查询或者 HTTP 请求。此时我们就不应该让事件处理逻辑在 IO 线程上执行，而是应该派发到线程池中去执行。原因也很简单，IO 线程主要用于接收请求，如果 IO 线程被占满，将导致它不能接收新的请求。</p><p>Dispatcher 就是线程派发器。需要说明的是，Dispatcher 真实的职责创建具有线程派发能力的 ChannelHandler，比如 AllChannelHandler、MessageOnlyChannelHandler 和 ExecutionChannelHandler 等，其本身并不具备线程派发能力。Dubbo 支持 5 种不同的线程派发策略，下面通过一个表格列举一下。</p><table><thead><tr><th align="center">策略</th><th align="center">用途</th></tr></thead><tbody><tr><td align="center">all</td><td align="center">所有消息都派发到线程池，包括请求，响应，连接事件，断开事件等</td></tr><tr><td align="center">direct</td><td align="center">所有消息都不派发到线程池，全部在 IO 线程上直接执行</td></tr><tr><td align="center">message</td><td align="center">只有<strong>请求</strong>和<strong>响应</strong>消息派发到线程池，其它消息均在 IO 线程上执行</td></tr><tr><td align="center">execution</td><td align="center">只有<strong>请求</strong>消息派发到线程池，不含响应。其它消息均在 IO 线程上执行</td></tr><tr><td align="center">connection</td><td align="center">在 IO 线程上，将连接断开事件放入队列，有序逐个执行，其它消息派发到线程池</td></tr></tbody></table><p>默认配置下，Dubbo 使用 <code>all</code> 派发策略，即将所有的消息都派发到线程池中。下面我们来分析一下 AllChannelHandler 的代码。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AllChannelHandler</span> <span class="keyword">extends</span> <span class="title class_">WrappedChannelHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">AllChannelHandler</span><span class="params">(ChannelHandler handler, URL url)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>(handler, url);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 处理连接事件 */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">connected</span><span class="params">(Channel channel)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="comment">// 获取线程池</span></span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">cexecutor</span> <span class="operator">=</span> getExecutorService();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 将连接事件派发到线程池中处理</span></span><br><span class="line">            cexecutor.execute(<span class="keyword">new</span> <span class="title class_">ChannelEventRunnable</span>(channel, handler, ChannelState.CONNECTED));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable t) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ExecutionException</span>(..., <span class="string">&quot; error when process connected event .&quot;</span>, t);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 处理断开事件 */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">disconnected</span><span class="params">(Channel channel)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">cexecutor</span> <span class="operator">=</span> getExecutorService();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            cexecutor.execute(<span class="keyword">new</span> <span class="title class_">ChannelEventRunnable</span>(channel, handler, ChannelState.DISCONNECTED));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable t) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ExecutionException</span>(..., <span class="string">&quot;error when process disconnected event .&quot;</span>, t);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 处理请求和响应消息，这里的 message 变量类型可能是 Request，也可能是 Response */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">received</span><span class="params">(Channel channel, Object message)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">cexecutor</span> <span class="operator">=</span> getExecutorService();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 将请求和响应消息派发到线程池中处理</span></span><br><span class="line">            cexecutor.execute(<span class="keyword">new</span> <span class="title class_">ChannelEventRunnable</span>(channel, handler, ChannelState.RECEIVED, message));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable t) &#123;</span><br><span class="line">            <span class="keyword">if</span>(message <span class="keyword">instanceof</span> Request &amp;&amp; t <span class="keyword">instanceof</span> RejectedExecutionException)&#123;</span><br><span class="line">                <span class="type">Request</span> <span class="variable">request</span> <span class="operator">=</span> (Request)message;</span><br><span class="line">                <span class="comment">// 如果通信方式为双向通信，此时将 Server side ... threadpool is exhausted </span></span><br><span class="line">                <span class="comment">// 错误信息封装到 Response 中，并返回给服务消费方。</span></span><br><span class="line">                <span class="keyword">if</span>(request.isTwoWay())&#123;</span><br><span class="line">                    <span class="type">String</span> <span class="variable">msg</span> <span class="operator">=</span> <span class="string">&quot;Server side(&quot;</span> + url.getIp() + <span class="string">&quot;,&quot;</span> + url.getPort() </span><br><span class="line">                        + <span class="string">&quot;) threadpool is exhausted ,detail msg:&quot;</span> + t.getMessage();</span><br><span class="line">                    <span class="type">Response</span> <span class="variable">response</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Response</span>(request.getId(), request.getVersion());</span><br><span class="line">                    response.setStatus(Response.SERVER_THREADPOOL_EXHAUSTED_ERROR);</span><br><span class="line">                    response.setErrorMessage(msg);</span><br><span class="line">                    <span class="comment">// 返回包含错误信息的 Response 对象</span></span><br><span class="line">                    channel.send(response);</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ExecutionException</span>(..., <span class="string">&quot; error when process received event .&quot;</span>, t);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 处理异常信息 */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">caught</span><span class="params">(Channel channel, Throwable exception)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">cexecutor</span> <span class="operator">=</span> getExecutorService();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            cexecutor.execute(<span class="keyword">new</span> <span class="title class_">ChannelEventRunnable</span>(channel, handler, ChannelState.CAUGHT, exception));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable t) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ExecutionException</span>(..., <span class="string">&quot;error when process caught event ...&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如上，请求对象会被封装 ChannelEventRunnable 中，ChannelEventRunnable 将会是服务调用过程的新起点。所以接下来我们以 ChannelEventRunnable 为起点向下探索。</p><h5 id="2-3-2-2-调用服务"><a href="#2-3-2-2-调用服务" class="headerlink" title="2.3.2.2 调用服务"></a>2.3.2.2 调用服务</h5><p>本小节，我们从 ChannelEventRunnable 开始分析，该类的主要代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ChannelEventRunnable</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ChannelHandler handler;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Channel channel;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ChannelState state;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Throwable exception;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Object message;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 检测通道状态，对于请求或响应消息，此时 state = RECEIVED</span></span><br><span class="line">        <span class="keyword">if</span> (state == ChannelState.RECEIVED) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// 将 channel 和 message 传给 ChannelHandler 对象，进行后续的调用</span></span><br><span class="line">                handler.received(channel, message);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                logger.warn(<span class="string">&quot;... operation error, channel is ... message is ...&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; </span><br><span class="line"></span><br><span class="line">        <span class="comment">// 其他消息类型通过 switch 进行处理</span></span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">switch</span> (state) &#123;</span><br><span class="line">            <span class="keyword">case</span> CONNECTED:</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    handler.connected(channel);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                    logger.warn(<span class="string">&quot;... operation error, channel is ...&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> DISCONNECTED:</span><br><span class="line">                <span class="comment">// ...</span></span><br><span class="line">            <span class="keyword">case</span> SENT:</span><br><span class="line">                <span class="comment">// ...</span></span><br><span class="line">            <span class="keyword">case</span> CAUGHT:</span><br><span class="line">                <span class="comment">// ...</span></span><br><span class="line">            <span class="keyword">default</span>:</span><br><span class="line">                logger.warn(<span class="string">&quot;unknown state: &quot;</span> + state + <span class="string">&quot;, message is &quot;</span> + message);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如上，请求和响应消息出现频率明显比其他类型消息高，所以这里对该类型的消息进行了针对性判断。ChannelEventRunnable 仅是一个中转站，它的 run 方法中并不包含具体的调用逻辑，仅用于将参数传给其他 ChannelHandler 对象进行处理，该对象类型为 DecodeHandler。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DecodeHandler</span> <span class="keyword">extends</span> <span class="title class_">AbstractChannelHandlerDelegate</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">DecodeHandler</span><span class="params">(ChannelHandler handler)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>(handler);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">received</span><span class="params">(Channel channel, Object message)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="keyword">if</span> (message <span class="keyword">instanceof</span> Decodeable) &#123;</span><br><span class="line">            <span class="comment">// 对 Decodeable 接口实现类对象进行解码</span></span><br><span class="line">            decode(message);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (message <span class="keyword">instanceof</span> Request) &#123;</span><br><span class="line">            <span class="comment">// 对 Request 的 data 字段进行解码</span></span><br><span class="line">            decode(((Request) message).getData());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (message <span class="keyword">instanceof</span> Response) &#123;</span><br><span class="line">            <span class="comment">// 对 Request 的 result 字段进行解码</span></span><br><span class="line">            decode(((Response) message).getResult());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 执行后续逻辑</span></span><br><span class="line">        handler.received(channel, message);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">decode</span><span class="params">(Object message)</span> &#123;</span><br><span class="line">        <span class="comment">// Decodeable 接口目前有两个实现类，</span></span><br><span class="line">        <span class="comment">// 分别为 DecodeableRpcInvocation 和 DecodeableRpcResult</span></span><br><span class="line">        <span class="keyword">if</span> (message != <span class="literal">null</span> &amp;&amp; message <span class="keyword">instanceof</span> Decodeable) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// 执行解码逻辑</span></span><br><span class="line">                ((Decodeable) message).decode();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Throwable e) &#123;</span><br><span class="line">                <span class="keyword">if</span> (log.isWarnEnabled()) &#123;</span><br><span class="line">                    log.warn(<span class="string">&quot;Call Decodeable.decode failed: &quot;</span> + e.getMessage(), e);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>DecodeHandler 主要是包含了一些解码逻辑。2.2.1 节分析请求解码时说过，请求解码可在 IO 线程上执行，也可在线程池中执行，这个取决于运行时配置。DecodeHandler 存在的意义就是保证请求或响应对象可在线程池中被解码。解码完毕后，完全解码后的 Request 对象会继续向后传递，下一站是 HeaderExchangeHandler。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HeaderExchangeHandler</span> <span class="keyword">implements</span> <span class="title class_">ChannelHandlerDelegate</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ExchangeHandler handler;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">HeaderExchangeHandler</span><span class="params">(ExchangeHandler handler)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (handler == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;handler == null&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">this</span>.handler = handler;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">received</span><span class="params">(Channel channel, Object message)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());</span><br><span class="line">        <span class="type">ExchangeChannel</span> <span class="variable">exchangeChannel</span> <span class="operator">=</span> HeaderExchangeChannel.getOrAddChannel(channel);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 处理请求对象</span></span><br><span class="line">            <span class="keyword">if</span> (message <span class="keyword">instanceof</span> Request) &#123;</span><br><span class="line">                <span class="type">Request</span> <span class="variable">request</span> <span class="operator">=</span> (Request) message;</span><br><span class="line">                <span class="keyword">if</span> (request.isEvent()) &#123;</span><br><span class="line">                    <span class="comment">// 处理事件</span></span><br><span class="line">                    handlerEvent(channel, request);</span><br><span class="line">                &#125; </span><br><span class="line">                <span class="comment">// 处理普通的请求</span></span><br><span class="line">                <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="comment">// 双向通信</span></span><br><span class="line">                    <span class="keyword">if</span> (request.isTwoWay()) &#123;</span><br><span class="line">                        <span class="comment">// 向后调用服务，并得到调用结果</span></span><br><span class="line">                        <span class="type">Response</span> <span class="variable">response</span> <span class="operator">=</span> handleRequest(exchangeChannel, request);</span><br><span class="line">                        <span class="comment">// 将调用结果返回给服务消费端</span></span><br><span class="line">                        channel.send(response);</span><br><span class="line">                    &#125; </span><br><span class="line">                    <span class="comment">// 如果是单向通信，仅向后调用指定服务即可，无需返回调用结果</span></span><br><span class="line">                    <span class="keyword">else</span> &#123;</span><br><span class="line">                        handler.received(exchangeChannel, request.getData());</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;      </span><br><span class="line">            <span class="comment">// 处理响应对象，服务消费方会执行此处逻辑，后面分析</span></span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (message <span class="keyword">instanceof</span> Response) &#123;</span><br><span class="line">                handleResponse(channel, (Response) message);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (message <span class="keyword">instanceof</span> String) &#123;</span><br><span class="line">                <span class="comment">// telnet 相关，忽略</span></span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                handler.received(exchangeChannel, message);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            HeaderExchangeChannel.removeChannelIfDisconnected(channel);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    Response <span class="title function_">handleRequest</span><span class="params">(ExchangeChannel channel, Request req)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="type">Response</span> <span class="variable">res</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Response</span>(req.getId(), req.getVersion());</span><br><span class="line">        <span class="comment">// 检测请求是否合法，不合法则返回状态码为 BAD_REQUEST 的响应</span></span><br><span class="line">        <span class="keyword">if</span> (req.isBroken()) &#123;</span><br><span class="line">            <span class="type">Object</span> <span class="variable">data</span> <span class="operator">=</span> req.getData();</span><br><span class="line"></span><br><span class="line">            String msg;</span><br><span class="line">            <span class="keyword">if</span> (data == <span class="literal">null</span>)</span><br><span class="line">                msg = <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span></span><br><span class="line">                (data <span class="keyword">instanceof</span> Throwable) msg = StringUtils.toString((Throwable) data);</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                msg = data.toString();</span><br><span class="line">            res.setErrorMessage(<span class="string">&quot;Fail to decode request due to: &quot;</span> + msg);</span><br><span class="line">            <span class="comment">// 设置 BAD_REQUEST 状态</span></span><br><span class="line">            res.setStatus(Response.BAD_REQUEST);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">return</span> res;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取 data 字段值，也就是 RpcInvocation 对象</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">msg</span> <span class="operator">=</span> req.getData();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 继续向下调用</span></span><br><span class="line">            <span class="type">Object</span> <span class="variable">result</span> <span class="operator">=</span> handler.reply(channel, msg);</span><br><span class="line">            <span class="comment">// 设置 OK 状态码</span></span><br><span class="line">            res.setStatus(Response.OK);</span><br><span class="line">            <span class="comment">// 设置调用结果</span></span><br><span class="line">            res.setResult(result);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable e) &#123;</span><br><span class="line">            <span class="comment">// 若调用过程出现异常，则设置 SERVICE_ERROR，表示服务端异常</span></span><br><span class="line">            res.setStatus(Response.SERVICE_ERROR);</span><br><span class="line">            res.setErrorMessage(StringUtils.toString(e));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>到这里，我们看到了比较清晰的请求和响应逻辑。对于双向通信，HeaderExchangeHandler 首先向后进行调用，得到调用结果。然后将调用结果封装到 Response 对象中，最后再将该对象返回给服务消费方。如果请求不合法，或者调用失败，则将错误信息封装到 Response 对象中，并返回给服务消费方。接下来我们继续向后分析，把剩余的调用过程分析完。下面分析定义在 DubboProtocol 类中的匿名类对象逻辑，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DubboProtocol</span> <span class="keyword">extends</span> <span class="title class_">AbstractProtocol</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">NAME</span> <span class="operator">=</span> <span class="string">&quot;dubbo&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">ExchangeHandler</span> <span class="variable">requestHandler</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ExchangeHandlerAdapter</span>() &#123;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> Object <span class="title function_">reply</span><span class="params">(ExchangeChannel channel, Object message)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">            <span class="keyword">if</span> (message <span class="keyword">instanceof</span> Invocation) &#123;</span><br><span class="line">                <span class="type">Invocation</span> <span class="variable">inv</span> <span class="operator">=</span> (Invocation) message;</span><br><span class="line">                <span class="comment">// 获取 Invoker 实例</span></span><br><span class="line">                Invoker&lt;?&gt; invoker = getInvoker(channel, inv);</span><br><span class="line">                <span class="keyword">if</span> (Boolean.TRUE.toString().equals(inv.getAttachments().get(IS_CALLBACK_SERVICE_INVOKE))) &#123;</span><br><span class="line">                    <span class="comment">// 回调相关，忽略</span></span><br><span class="line">                &#125;</span><br><span class="line">                RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());</span><br><span class="line">                <span class="comment">// 通过 Invoker 调用具体的服务</span></span><br><span class="line">                <span class="keyword">return</span> invoker.invoke(inv);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RemotingException</span>(channel, <span class="string">&quot;Unsupported request: ...&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 忽略其他方法</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    Invoker&lt;?&gt; getInvoker(Channel channel, Invocation inv) <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="comment">// 忽略回调和本地存根相关逻辑</span></span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line">        <span class="type">int</span> <span class="variable">port</span> <span class="operator">=</span> channel.getLocalAddress().getPort();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 计算 service key，格式为 groupName/serviceName:serviceVersion:port。比如：</span></span><br><span class="line">        <span class="comment">//   dubbo/com.alibaba.dubbo.demo.DemoService:1.0.0:20880</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">serviceKey</span> <span class="operator">=</span> serviceKey(port, path, inv.getAttachments().get(Constants.VERSION_KEY), inv.getAttachments().get(Constants.GROUP_KEY));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 从 exporterMap 查找与 serviceKey 相对应的 DubboExporter 对象，</span></span><br><span class="line">        <span class="comment">// 服务导出过程中会将 &lt;serviceKey, DubboExporter&gt; 映射关系存储到 exporterMap 集合中</span></span><br><span class="line">        DubboExporter&lt;?&gt; exporter = (DubboExporter&lt;?&gt;) exporterMap.get(serviceKey);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (exporter == <span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RemotingException</span>(channel, <span class="string">&quot;Not found exported service ...&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取 Invoker 对象，并返回</span></span><br><span class="line">        <span class="keyword">return</span> exporter.getInvoker();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 忽略其他方法</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上逻辑用于获取与指定服务对应的 Invoker 实例，并通过 Invoker 的 invoke 方法调用服务逻辑。invoke 方法定义在 AbstractProxyInvoker 中，代码如下。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">AbstractProxyInvoker</span>&lt;T&gt; <span class="keyword">implements</span> <span class="title class_">Invoker</span>&lt;T&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">invoke</span><span class="params">(Invocation invocation)</span> <span class="keyword">throws</span> RpcException &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 调用 doInvoke 执行后续的调用，并将调用结果封装到 RpcResult 中，并</span></span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RpcResult</span>(doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InvocationTargetException e) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RpcResult</span>(e.getTargetException());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RpcException</span>(<span class="string">&quot;Failed to invoke remote proxy method ...&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">abstract</span> Object <span class="title function_">doInvoke</span><span class="params">(T proxy, String methodName, Class&lt;?&gt;[] parameterTypes, Object[] arguments)</span> <span class="keyword">throws</span> Throwable;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如上，doInvoke 是一个抽象方法，这个需要由具体的 Invoker 实例实现。Invoker 实例是在运行时通过 JavassistProxyFactory 创建的，创建逻辑如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JavassistProxyFactory</span> <span class="keyword">extends</span> <span class="title class_">AbstractProxyFactory</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 省略其他方法</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> &lt;T&gt; Invoker&lt;T&gt; <span class="title function_">getInvoker</span><span class="params">(T proxy, Class&lt;T&gt; type, URL url)</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">Wrapper</span> <span class="variable">wrapper</span> <span class="operator">=</span> Wrapper.getWrapper(proxy.getClass().getName().indexOf(<span class="string">&#x27;$&#x27;</span>) &lt; <span class="number">0</span> ? proxy.getClass() : type);</span><br><span class="line">        <span class="comment">// 创建匿名类对象</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">AbstractProxyInvoker</span>&lt;T&gt;(proxy, type, url) &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">protected</span> Object <span class="title function_">doInvoke</span><span class="params">(T proxy, String methodName,</span></span><br><span class="line"><span class="params">                                      Class&lt;?&gt;[] parameterTypes,</span></span><br><span class="line"><span class="params">                                      Object[] arguments)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">                <span class="comment">// 调用 invokeMethod 方法进行后续的调用</span></span><br><span class="line">                <span class="keyword">return</span> wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Wrapper 是一个抽象类，其中 invokeMethod 是一个抽象方法。Dubbo 会在运行时通过 Javassist 框架为 Wrapper 生成实现类，并实现 invokeMethod 方法，该方法最终会根据调用信息调用具体的服务。以 DemoServiceImpl 为例，Javassist 为其生成的代理类如下。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** Wrapper0 是在运行时生成的，大家可使用 Arthas 进行反编译 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Wrapper0</span> <span class="keyword">extends</span> <span class="title class_">Wrapper</span> <span class="keyword">implements</span> <span class="title class_">ClassGenerator</span>.DC &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String[] pns;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Map pts;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String[] mns;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String[] dmns;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Class[] mts0;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 省略其他方法</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">invokeMethod</span><span class="params">(Object object, String string, Class[] arrclass, Object[] arrobject)</span> <span class="keyword">throws</span> InvocationTargetException &#123;</span><br><span class="line">        DemoService demoService;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 类型转换</span></span><br><span class="line">            demoService = (DemoService)object;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span> (Throwable throwable) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(throwable);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 根据方法名调用指定的方法</span></span><br><span class="line">            <span class="keyword">if</span> (<span class="string">&quot;sayHello&quot;</span>.equals(string) &amp;&amp; arrclass.length == <span class="number">1</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> demoService.sayHello((String)arrobject[<span class="number">0</span>]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span> (Throwable throwable) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">InvocationTargetException</span>(throwable);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NoSuchMethodException</span>(<span class="keyword">new</span> <span class="title class_">StringBuffer</span>().append(<span class="string">&quot;Not found method \&quot;&quot;</span>).append(string).append(<span class="string">&quot;\&quot; in class com.alibaba.dubbo.demo.DemoService.&quot;</span>).toString());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>到这里，整个服务调用过程就分析完了。最后把调用过程贴出来，如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">ChannelEventRunnable#run()</span><br><span class="line">  —&gt; DecodeHandler#received(Channel, Object)</span><br><span class="line">    —&gt; HeaderExchangeHandler#received(Channel, Object)</span><br><span class="line">      —&gt; HeaderExchangeHandler#handleRequest(ExchangeChannel, Request)</span><br><span class="line">        —&gt; DubboProtocol.requestHandler#reply(ExchangeChannel, Object)</span><br><span class="line">          —&gt; Filter#invoke(Invoker, Invocation)</span><br><span class="line">            —&gt; AbstractProxyInvoker#invoke(Invocation)</span><br><span class="line">              —&gt; Wrapper#invokeMethod(Object, String, Class[], Object[])</span><br><span class="line">                —&gt; DemoServiceImpl#sayHello(String)</span><br></pre></td></tr></table></figure><h3 id="2-4-服务提供方响应请求"><a href="#2-4-服务提供方响应请求" class="headerlink" title="2.4 服务提供方响应请求"></a>2.4 服务提供方响应请求</h3><p>服务提供方调用指定服务后，会将调用结果封装到 Response 对象中，并将该对象返回给服务消费方。服务提供方也是通过 NettyChannel 的 send 方法将 Response 对象返回，这个方法在 2.2.1 节分析过，这里就不在重复分析了。本节我们仅需关注 Response 对象的编码过程即可，这里仍然省略一些中间调用，直接分析具体的编码逻辑。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ExchangeCodec</span> <span class="keyword">extends</span> <span class="title class_">TelnetCodec</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">encode</span><span class="params">(Channel channel, ChannelBuffer buffer, Object msg)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="keyword">if</span> (msg <span class="keyword">instanceof</span> Request) &#123;</span><br><span class="line">            encodeRequest(channel, buffer, (Request) msg);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (msg <span class="keyword">instanceof</span> Response) &#123;</span><br><span class="line">            <span class="comment">// 对响应对象进行编码</span></span><br><span class="line">            encodeResponse(channel, buffer, (Response) msg);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="built_in">super</span>.encode(channel, buffer, msg);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">encodeResponse</span><span class="params">(Channel channel, ChannelBuffer buffer, Response res)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">savedWriteIndex</span> <span class="operator">=</span> buffer.writerIndex();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">Serialization</span> <span class="variable">serialization</span> <span class="operator">=</span> getSerialization(channel);</span><br><span class="line">            <span class="comment">// 创建消息头字节数组</span></span><br><span class="line">            <span class="type">byte</span>[] header = <span class="keyword">new</span> <span class="title class_">byte</span>[HEADER_LENGTH];</span><br><span class="line">            <span class="comment">// 设置魔数</span></span><br><span class="line">            Bytes.short2bytes(MAGIC, header);</span><br><span class="line">            <span class="comment">// 设置序列化器编号</span></span><br><span class="line">            header[<span class="number">2</span>] = serialization.getContentTypeId();</span><br><span class="line">            <span class="keyword">if</span> (res.isHeartbeat()) header[<span class="number">2</span>] |= FLAG_EVENT;</span><br><span class="line">            <span class="comment">// 获取响应状态</span></span><br><span class="line">            <span class="type">byte</span> <span class="variable">status</span> <span class="operator">=</span> res.getStatus();</span><br><span class="line">            <span class="comment">// 设置响应状态</span></span><br><span class="line">            header[<span class="number">3</span>] = status;</span><br><span class="line">            <span class="comment">// 设置请求编号</span></span><br><span class="line">            Bytes.long2bytes(res.getId(), header, <span class="number">4</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 更新 writerIndex，为消息头预留 16 个字节的空间</span></span><br><span class="line">            buffer.writerIndex(savedWriteIndex + HEADER_LENGTH);</span><br><span class="line">            <span class="type">ChannelBufferOutputStream</span> <span class="variable">bos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ChannelBufferOutputStream</span>(buffer);</span><br><span class="line">            <span class="type">ObjectOutput</span> <span class="variable">out</span> <span class="operator">=</span> serialization.serialize(channel.getUrl(), bos);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (status == Response.OK) &#123;</span><br><span class="line">                <span class="keyword">if</span> (res.isHeartbeat()) &#123;</span><br><span class="line">                    <span class="comment">// 对心跳响应结果进行序列化，已废弃</span></span><br><span class="line">                    encodeHeartbeatData(channel, out, res.getResult());</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="comment">// 对调用结果进行序列化</span></span><br><span class="line">                    encodeResponseData(channel, out, res.getResult(), res.getVersion());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123; </span><br><span class="line">                <span class="comment">// 对错误信息进行序列化</span></span><br><span class="line">                out.writeUTF(res.getErrorMessage())</span><br><span class="line">            &#125;;</span><br><span class="line">            out.flushBuffer();</span><br><span class="line">            <span class="keyword">if</span> (out <span class="keyword">instanceof</span> Cleanable) &#123;</span><br><span class="line">                ((Cleanable) out).cleanup();</span><br><span class="line">            &#125;</span><br><span class="line">            bos.flush();</span><br><span class="line">            bos.close();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 获取写入的字节数，也就是消息体长度</span></span><br><span class="line">            <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> bos.writtenBytes();</span><br><span class="line">            checkPayload(channel, len);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 将消息体长度写入到消息头中</span></span><br><span class="line">            Bytes.int2bytes(len, header, <span class="number">12</span>);</span><br><span class="line">            <span class="comment">// 将 buffer 指针移动到 savedWriteIndex，为写消息头做准备</span></span><br><span class="line">            buffer.writerIndex(savedWriteIndex);</span><br><span class="line">            <span class="comment">// 从 savedWriteIndex 下标处写入消息头</span></span><br><span class="line">            buffer.writeBytes(header); </span><br><span class="line">            <span class="comment">// 设置新的 writerIndex，writerIndex = 原写下标 + 消息头长度 + 消息体长度</span></span><br><span class="line">            buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + len);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable t) &#123;</span><br><span class="line">            <span class="comment">// 异常处理逻辑不是很难理解，但是代码略多，这里忽略了</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DubboCodec</span> <span class="keyword">extends</span> <span class="title class_">ExchangeCodec</span> <span class="keyword">implements</span> <span class="title class_">Codec2</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">encodeResponseData</span><span class="params">(Channel channel, ObjectOutput out, Object data, String version)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">Result</span> <span class="variable">result</span> <span class="operator">=</span> (Result) data;</span><br><span class="line">        <span class="comment">// 检测当前协议版本是否支持带有 attachments 集合的 Response 对象</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">attach</span> <span class="operator">=</span> Version.isSupportResponseAttachment(version);</span><br><span class="line">        <span class="type">Throwable</span> <span class="variable">th</span> <span class="operator">=</span> result.getException();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 异常信息为空</span></span><br><span class="line">        <span class="keyword">if</span> (th == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="type">Object</span> <span class="variable">ret</span> <span class="operator">=</span> result.getValue();</span><br><span class="line">            <span class="comment">// 调用结果为空</span></span><br><span class="line">            <span class="keyword">if</span> (ret == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="comment">// 序列化响应类型</span></span><br><span class="line">                out.writeByte(attach ? RESPONSE_NULL_VALUE_WITH_ATTACHMENTS : RESPONSE_NULL_VALUE);</span><br><span class="line">            &#125; </span><br><span class="line">            <span class="comment">// 调用结果非空</span></span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 序列化响应类型</span></span><br><span class="line">                out.writeByte(attach ? RESPONSE_VALUE_WITH_ATTACHMENTS : RESPONSE_VALUE);</span><br><span class="line">                <span class="comment">// 序列化调用结果</span></span><br><span class="line">                out.writeObject(ret);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; </span><br><span class="line">        <span class="comment">// 异常信息非空</span></span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 序列化响应类型</span></span><br><span class="line">            out.writeByte(attach ? RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS : RESPONSE_WITH_EXCEPTION);</span><br><span class="line">            <span class="comment">// 序列化异常对象</span></span><br><span class="line">            out.writeObject(th);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (attach) &#123;</span><br><span class="line">            <span class="comment">// 记录 Dubbo 协议版本</span></span><br><span class="line">            result.getAttachments().put(Constants.DUBBO_VERSION_KEY, Version.getProtocolVersion());</span><br><span class="line">            <span class="comment">// 序列化 attachments 集合</span></span><br><span class="line">            out.writeObject(result.getAttachments());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上就是 Response 对象编码的过程，和前面分析的 Request 对象编码过程很相似。如果大家能看 Request 对象的编码逻辑，那么这里的 Response 对象的编码逻辑也不难理解，就不多说了。接下来我们再来分析双向通信的最后一环 —— 服务消费方接收调用结果。</p><h3 id="2-5-服务消费方接收调用结果"><a href="#2-5-服务消费方接收调用结果" class="headerlink" title="2.5 服务消费方接收调用结果"></a>2.5 服务消费方接收调用结果</h3><p>服务消费方在收到响应数据后，首先要做的事情是对响应数据进行解码，得到 Response 对象。然后再将该对象传给下一个入站处理器，这个入站处理器就是 NettyHandler。接下来 NettyHandler 会将这个对象继续向下传递，最后 AllChannelHandler 的 received 方法会收到这个对象，并将这个对象派发到线程池中。这个过程和服务提供方接收请求的过程是一样的，因此这里就不重复分析了。本节我们重点分析两个方面的内容，一是响应数据的解码过程，二是 Dubbo 如何将调用结果传递给用户线程的。下面先来分析响应数据的解码过程。</p><h4 id="2-5-1-响应数据解码"><a href="#2-5-1-响应数据解码" class="headerlink" title="2.5.1 响应数据解码"></a>2.5.1 响应数据解码</h4><p>响应数据解码逻辑主要的逻辑封装在 DubboCodec 中，我们直接分析这个类的代码。如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DubboCodec</span> <span class="keyword">extends</span> <span class="title class_">ExchangeCodec</span> <span class="keyword">implements</span> <span class="title class_">Codec2</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> Object <span class="title function_">decodeBody</span><span class="params">(Channel channel, InputStream is, <span class="type">byte</span>[] header)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">byte</span> <span class="variable">flag</span> <span class="operator">=</span> header[<span class="number">2</span>], proto = (<span class="type">byte</span>) (flag &amp; SERIALIZATION_MASK);</span><br><span class="line">        <span class="type">Serialization</span> <span class="variable">s</span> <span class="operator">=</span> CodecSupport.getSerialization(channel.getUrl(), proto);</span><br><span class="line">        <span class="comment">// 获取请求编号</span></span><br><span class="line">        <span class="type">long</span> <span class="variable">id</span> <span class="operator">=</span> Bytes.bytes2long(header, <span class="number">4</span>);</span><br><span class="line">        <span class="comment">// 检测消息类型，若下面的条件成立，表明消息类型为 Response</span></span><br><span class="line">        <span class="keyword">if</span> ((flag &amp; FLAG_REQUEST) == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">// 创建 Response 对象</span></span><br><span class="line">            <span class="type">Response</span> <span class="variable">res</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Response</span>(id);</span><br><span class="line">            <span class="comment">// 检测事件标志位</span></span><br><span class="line">            <span class="keyword">if</span> ((flag &amp; FLAG_EVENT) != <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="comment">// 设置心跳事件</span></span><br><span class="line">                res.setEvent(Response.HEARTBEAT_EVENT);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 获取响应状态</span></span><br><span class="line">            <span class="type">byte</span> <span class="variable">status</span> <span class="operator">=</span> header[<span class="number">3</span>];</span><br><span class="line">            <span class="comment">// 设置响应状态</span></span><br><span class="line">            res.setStatus(status);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 如果响应状态为 OK，表明调用过程正常</span></span><br><span class="line">            <span class="keyword">if</span> (status == Response.OK) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    Object data;</span><br><span class="line">                    <span class="keyword">if</span> (res.isHeartbeat()) &#123;</span><br><span class="line">                        <span class="comment">// 反序列化心跳数据，已废弃</span></span><br><span class="line">                        data = decodeHeartbeatData(channel, deserialize(s, channel.getUrl(), is));</span><br><span class="line">                    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (res.isEvent()) &#123;</span><br><span class="line">                        <span class="comment">// 反序列化事件数据</span></span><br><span class="line">                        data = decodeEventData(channel, deserialize(s, channel.getUrl(), is));</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        DecodeableRpcResult result;</span><br><span class="line">                        <span class="comment">// 根据 url 参数决定是否在 IO 线程上执行解码逻辑</span></span><br><span class="line">                        <span class="keyword">if</span> (channel.getUrl().getParameter(</span><br><span class="line">                                Constants.DECODE_IN_IO_THREAD_KEY,</span><br><span class="line">                                Constants.DEFAULT_DECODE_IN_IO_THREAD)) &#123;</span><br><span class="line">                            <span class="comment">// 创建 DecodeableRpcResult 对象</span></span><br><span class="line">                            result = <span class="keyword">new</span> <span class="title class_">DecodeableRpcResult</span>(channel, res, is,</span><br><span class="line">                                    (Invocation) getRequestData(id), proto);</span><br><span class="line">                            <span class="comment">// 进行后续的解码工作</span></span><br><span class="line">                            result.decode();</span><br><span class="line">                        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                            <span class="comment">// 创建 DecodeableRpcResult 对象</span></span><br><span class="line">                            result = <span class="keyword">new</span> <span class="title class_">DecodeableRpcResult</span>(channel, res,</span><br><span class="line">                                    <span class="keyword">new</span> <span class="title class_">UnsafeByteArrayInputStream</span>(readMessageData(is)),</span><br><span class="line">                                    (Invocation) getRequestData(id), proto);</span><br><span class="line">                        &#125;</span><br><span class="line">                        data = result;</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 设置 DecodeableRpcResult 对象到 Response 对象中</span></span><br><span class="line">                    res.setResult(data);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Throwable t) &#123;</span><br><span class="line">                    <span class="comment">// 解码过程中出现了错误，此时设置 CLIENT_ERROR 状态码到 Response 对象中</span></span><br><span class="line">                    res.setStatus(Response.CLIENT_ERROR);</span><br><span class="line">                    res.setErrorMessage(StringUtils.toString(t));</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; </span><br><span class="line">            <span class="comment">// 响应状态非 OK，表明调用过程出现了异常</span></span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 反序列化异常信息，并设置到 Response 对象中</span></span><br><span class="line">                res.setErrorMessage(deserialize(s, channel.getUrl(), is).readUTF());</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> res;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 对请求数据进行解码，前面已分析过，此处忽略</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上就是响应数据的解码过程，上面逻辑看起来是不是似曾相识。对的，我们在前面章节分析过 DubboCodec 的 decodeBody 方法中关于请求数据的解码过程，该过程和响应数据的解码过程很相似。下面，我们继续分析调用结果的反序列化过程，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DecodeableRpcResult</span> <span class="keyword">extends</span> <span class="title class_">RpcResult</span> <span class="keyword">implements</span> <span class="title class_">Codec</span>, Decodeable &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Invocation invocation;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">decode</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="keyword">if</span> (!hasDecoded &amp;&amp; channel != <span class="literal">null</span> &amp;&amp; inputStream != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// 执行反序列化操作</span></span><br><span class="line">                decode(channel, inputStream);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Throwable e) &#123;</span><br><span class="line">                <span class="comment">// 反序列化失败，设置 CLIENT_ERROR 状态到 Response 对象中</span></span><br><span class="line">                response.setStatus(Response.CLIENT_ERROR);</span><br><span class="line">                <span class="comment">// 设置异常信息</span></span><br><span class="line">                response.setErrorMessage(StringUtils.toString(e));</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                hasDecoded = <span class="literal">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">decode</span><span class="params">(Channel channel, InputStream input)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">ObjectInput</span> <span class="variable">in</span> <span class="operator">=</span> CodecSupport.getSerialization(channel.getUrl(), serializationType)</span><br><span class="line">                .deserialize(channel.getUrl(), input);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 反序列化响应类型</span></span><br><span class="line">        <span class="type">byte</span> <span class="variable">flag</span> <span class="operator">=</span> in.readByte();</span><br><span class="line">        <span class="keyword">switch</span> (flag) &#123;</span><br><span class="line">            <span class="keyword">case</span> DubboCodec.RESPONSE_NULL_VALUE:</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> DubboCodec.RESPONSE_VALUE:</span><br><span class="line">                <span class="comment">// ...</span></span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> DubboCodec.RESPONSE_WITH_EXCEPTION:</span><br><span class="line">                <span class="comment">// ...</span></span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 返回值为空，且携带了 attachments 集合</span></span><br><span class="line">            <span class="keyword">case</span> DubboCodec.RESPONSE_NULL_VALUE_WITH_ATTACHMENTS:</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">// 反序列化 attachments 集合，并存储起来 </span></span><br><span class="line">                    setAttachments((Map&lt;String, String&gt;) in.readObject(Map.class));</span><br><span class="line">                &#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IOException</span>(StringUtils.toString(<span class="string">&quot;Read response data failed.&quot;</span>, e));</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 返回值不为空，且携带了 attachments 集合</span></span><br><span class="line">            <span class="keyword">case</span> DubboCodec.RESPONSE_VALUE_WITH_ATTACHMENTS:</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">// 获取返回值类型</span></span><br><span class="line">                    Type[] returnType = RpcUtils.getReturnTypes(invocation);</span><br><span class="line">                    <span class="comment">// 反序列化调用结果，并保存起来</span></span><br><span class="line">                    setValue(returnType == <span class="literal">null</span> || returnType.length == <span class="number">0</span> ? in.readObject() :</span><br><span class="line">                            (returnType.length == <span class="number">1</span> ? in.readObject((Class&lt;?&gt;) returnType[<span class="number">0</span>])</span><br><span class="line">                                    : in.readObject((Class&lt;?&gt;) returnType[<span class="number">0</span>], returnType[<span class="number">1</span>])));</span><br><span class="line">                    <span class="comment">// 反序列化 attachments 集合，并存储起来</span></span><br><span class="line">                    setAttachments((Map&lt;String, String&gt;) in.readObject(Map.class));</span><br><span class="line">                &#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IOException</span>(StringUtils.toString(<span class="string">&quot;Read response data failed.&quot;</span>, e));</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 异常对象不为空，且携带了 attachments 集合</span></span><br><span class="line">            <span class="keyword">case</span> DubboCodec.RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS:</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">// 反序列化异常对象</span></span><br><span class="line">                    <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> in.readObject();</span><br><span class="line">                    <span class="keyword">if</span> (obj <span class="keyword">instanceof</span> Throwable == <span class="literal">false</span>)</span><br><span class="line">                        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IOException</span>(<span class="string">&quot;Response data error, expect Throwable, but get &quot;</span> + obj);</span><br><span class="line">                    <span class="comment">// 设置异常对象</span></span><br><span class="line">                    setException((Throwable) obj);</span><br><span class="line">                    <span class="comment">// 反序列化 attachments 集合，并存储起来</span></span><br><span class="line">                    setAttachments((Map&lt;String, String&gt;) in.readObject(Map.class));</span><br><span class="line">                &#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IOException</span>(StringUtils.toString(<span class="string">&quot;Read response data failed.&quot;</span>, e));</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">default</span>:</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IOException</span>(<span class="string">&quot;Unknown result flag, expect &#x27;0&#x27; &#x27;1&#x27; &#x27;2&#x27;, get &quot;</span> + flag);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (in <span class="keyword">instanceof</span> Cleanable) &#123;</span><br><span class="line">            ((Cleanable) in).cleanup();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>本篇文章所分析的源码版本为 2.6.4，该版本下的 Response 支持 attachments 集合，所以上面仅对部分 case 分支进行了注释。其他 case 分支的逻辑比被注释分支的逻辑更为简单，这里就忽略了。我们所使用的测试服务接口 DemoService 包含了一个具有返回值的方法，所以正常调用下，线程会进入 RESPONSE_VALUE_WITH_ATTACHMENTS 分支中。然后线程会从 invocation 变量（大家探索一下 invocation 变量的由来）中获取返回值类型，接着对调用结果进行反序列化，并将序列化后的结果存储起来。最后对 attachments 集合进行反序列化，并存到指定字段中。到此，关于响应数据的解码过程就分析完了。接下来，我们再来探索一下响应对象 Response 的去向。</p><h4 id="2-5-2-向用户线程传递调用结果"><a href="#2-5-2-向用户线程传递调用结果" class="headerlink" title="2.5.2 向用户线程传递调用结果"></a>2.5.2 向用户线程传递调用结果</h4><p>响应数据解码完成后，Dubbo 会将响应对象派发到线程池上。要注意的是，线程池中的线程并非用户的调用线程，所以要想办法将响应对象从线程池线程传递到用户线程上。我们在 2.1 节分析过用户线程在发送完请求后的动作，即调用 DefaultFuture 的 get 方法等待响应对象的到来。当响应对象到来后，用户线程会被唤醒，并通过<strong>调用编号</strong>获取属于自己的响应对象。下面我们就来看一下整个过程对应的代码。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HeaderExchangeHandler</span> <span class="keyword">implements</span> <span class="title class_">ChannelHandlerDelegate</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">received</span><span class="params">(Channel channel, Object message)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());</span><br><span class="line">        <span class="type">ExchangeChannel</span> <span class="variable">exchangeChannel</span> <span class="operator">=</span> HeaderExchangeChannel.getOrAddChannel(channel);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (message <span class="keyword">instanceof</span> Request) &#123;</span><br><span class="line">                <span class="comment">// 处理请求，前面已分析过，省略</span></span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (message <span class="keyword">instanceof</span> Response) &#123;</span><br><span class="line">                <span class="comment">// 处理响应</span></span><br><span class="line">                handleResponse(channel, (Response) message);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (message <span class="keyword">instanceof</span> String) &#123;</span><br><span class="line">                <span class="comment">// telnet 相关，忽略</span></span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                handler.received(exchangeChannel, message);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            HeaderExchangeChannel.removeChannelIfDisconnected(channel);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">handleResponse</span><span class="params">(Channel channel, Response response)</span> <span class="keyword">throws</span> RemotingException &#123;</span><br><span class="line">        <span class="keyword">if</span> (response != <span class="literal">null</span> &amp;&amp; !response.isHeartbeat()) &#123;</span><br><span class="line">            <span class="comment">// 继续向下调用</span></span><br><span class="line">            DefaultFuture.received(channel, response);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DefaultFuture</span> <span class="keyword">implements</span> <span class="title class_">ResponseFuture</span> &#123;  </span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Lock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Condition</span> <span class="variable">done</span> <span class="operator">=</span> lock.newCondition();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> Response response;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">received</span><span class="params">(Channel channel, Response response)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 根据调用编号从 FUTURES 集合中查找指定的 DefaultFuture 对象</span></span><br><span class="line">            <span class="type">DefaultFuture</span> <span class="variable">future</span> <span class="operator">=</span> FUTURES.remove(response.getId());</span><br><span class="line">            <span class="keyword">if</span> (future != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="comment">// 继续向下调用</span></span><br><span class="line">                future.doReceived(response);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                logger.warn(<span class="string">&quot;The timeout response finally returned at ...&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            CHANNELS.remove(response.getId());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">doReceived</span><span class="params">(Response res)</span> &#123;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 保存响应对象</span></span><br><span class="line">            response = res;</span><br><span class="line">            <span class="keyword">if</span> (done != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="comment">// 唤醒用户线程</span></span><br><span class="line">                done.signal();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (callback != <span class="literal">null</span>) &#123;</span><br><span class="line">            invokeCallback(callback);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上逻辑是将响应对象保存到相应的 DefaultFuture 实例中，然后再唤醒用户线程，随后用户线程即可从 DefaultFuture 实例中获取到相应结果。</p><p>本篇文章在多个地方都强调过调用编号很重要，但一直没有解释原因，这里简单说明一下。一般情况下，服务消费方会并发调用多个服务，每个用户线程发送请求后，会调用不同 DefaultFuture 对象的 get 方法进行等待。 一段时间后，服务消费方的线程池会收到多个响应对象。这个时候要考虑一个问题，如何将每个响应对象传递给相应的 DefaultFuture 对象，且不出错。答案是通过调用编号。DefaultFuture 被创建时，会要求传入一个 Request 对象。此时 DefaultFuture 可从 Request 对象中获取调用编号，并将 &lt;调用编号, DefaultFuture 对象&gt; 映射关系存入到静态 Map 中，即 FUTURES。线程池中的线程在收到 Response 对象后，会根据 Response 对象中的调用编号到 FUTURES 集合中取出相应的 DefaultFuture 对象，然后再将 Response 对象设置到 DefaultFuture 对象中。最后再唤醒用户线程，这样用户线程即可从 DefaultFuture 对象中获取调用结果了。</p><h2 id="3-总结"><a href="#3-总结" class="headerlink" title="3. 总结"></a>3. 总结</h2><p>本篇文章主要对 Dubbo 中的几种服务调用方式，以及从双向通信的角度对整个通信过程进行了详细的分析。按照通信顺序，通信过程包括服务消费方发送请求，服务提供方接收请求，服务提供方返回响应数据，服务消费方接收响应数据等过程。理解这些过程需要大家对网络编程，尤其是 Netty 有一定的了解。限于篇幅原因，本篇文章无法将服务调用的所有内容都一一进行分析。对于本篇文章未讲到或未详细分析的内容，比如服务降级、过滤器链、以及序列化等。大家若感兴趣，可自行进行分析。并将分析整理成文，分享给社区。</p></article><div class="post-copyright"><div class="copyright-cc-box"><i class="anzhiyufont anzhiyu-icon-copyright"></i></div><div class="post-copyright__author_box"><a class="post-copyright__author_img" href="/" title="头像"><img class="post-copyright__author_img_back" src= "" onerror="this.onerror=null,this.src=&quot;/img/404.jpg&quot;" data-lazy-src="https://q1.qlogo.cn/g?b=qq&amp;nk=2071916845&amp;s=640" title="头像" alt="头像"><img class="post-copyright__author_img_front" src= "" onerror="this.onerror=null,this.src=&quot;/img/404.jpg&quot;" data-lazy-src="https://q1.qlogo.cn/g?b=qq&amp;nk=2071916845&amp;s=640" title="头像" alt="头像"></a><div class="post-copyright__author_name">云少</div><div class="post-copyright__author_desc">站在巨人的肩膀罢了</div></div><div class="post-copyright__post__info"><a class="post-copyright__original" title="该文章为原创文章，注意版权协议" href="https://it985.github.io/posts/2ad0f5ed.html">原创</a><a class="post-copyright-title"><span onclick='rm.copyPageUrl("https://it985.github.io/posts/2ad0f5ed.html")'>Dubbo 源码分析 – 服务调用过程</span></a></div><div class="post-tools" id="post-tools"><div class="post-tools-left"><div class="rewardLeftButton"><div class="post-reward" onclick="anzhiyu.addRewardMask()"><div class="reward-button button--animated" title="赞赏作者"><i class="anzhiyufont anzhiyu-icon-hand-heart-fill"></i>打赏作者</div><div class="reward-main"><div class="reward-all"><span class="reward-title">感谢你赐予我前进的力量</span><ul class="reward-group"><li class="reward-item"><a href="/img/wxpay.webp" target="_blank"><img class="post-qr-code-img" src= "" onerror="this.onerror=null,this.src=&quot;/img/404.jpg&quot;" data-lazy-src="/img/wxpay.webp" alt="wechat"></a><div class="post-qr-code-desc">wechat</div></li><li class="reward-item"><a href="/img/alipay.webp" target="_blank"><img class="post-qr-code-img" src= "" onerror="this.onerror=null,this.src=&quot;/img/404.jpg&quot;" data-lazy-src="/img/alipay.webp" alt="alipay"></a><div class="post-qr-code-desc">alipay</div></li></ul><a class="reward-main-btn" href="/about/#about-reward" target="_blank"><div class="reward-text">赞赏者名单</div><div class="reward-dec">因为你们的支持让我意识到写文章的价值🙏</div></a></div></div></div><div id="quit-box" onclick="anzhiyu.removeRewardMask()" style="display:none"></div></div><div class="shareRight"><div class="share-link mobile"><div class="share-qrcode"><div class="share-button" title="使用手机访问这篇文章"><i class="anzhiyufont anzhiyu-icon-qrcode"></i></div><div class="share-main"><div class="share-main-all"><div id="qrcode" title="https://it985.github.io/posts/2ad0f5ed.html"></div><div class="reward-dec">使用手机访问这篇文章</div></div></div></div></div><div class="share-link weibo"><a class="share-button" target="_blank" href="https://service.weibo.com/share/share.php?title=Dubbo 源码分析 – 服务调用过程&amp;url=https://it985.github.io/posts/2ad0f5ed.html&amp;pic=https://www.bing.com/th?id=OHR.GlassOctopus_EN-GB6500292444_UHD.jpg" rel="external nofollow noreferrer noopener"><i class="anzhiyufont anzhiyu-icon-weibo"></i></a></div><div class="share-link copyurl"><div class="share-button" id="post-share-url" title="复制链接" onclick="rm.copyPageUrl()"><i class="anzhiyufont anzhiyu-icon-link"></i></div></div></div></div></div><div class="post-copyright__notice"><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" rel="external nofollow noreferrer" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://it985.github.io" target="_blank">云少IT</a>！</span></div></div><div class="post-tools-right"><div class="tag_share"><div class="post-meta__box"><div class="post-meta__box__tag-list"></div></div><div class="post_share"><div class="social-share" data-image="https://img02.anheyu.com/adminuploads/1/2022/09/05/6315e146a8bbd.webp" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.cbd.int/butterfly-extsrc@1.1.3/sharejs/dist/css/share.min.css" media="print" onload='this.media="all"'><script src="https://cdn.cbd.int/butterfly-extsrc@1.1.3/sharejs/dist/js/social-share.min.js" defer></script></div></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/posts/c3adb84d.html"><img class="prev-cover" src= "" onerror="this.onerror=null,this.src=&quot;/img/404.jpg&quot;" data-lazy-src="https://www.bing.com/th?id=OHR.WychwoodForest_EN-GB9336729827_UHD.jpg" onerror='onerror=null,src="/img/404.jpg"' alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">Dubbo 源码分析 – 集群容错之 LoadBalance</div></div></a></div><div class="next-post pull-right"><a href="/posts/b70bdabe.html"><img class="next-cover" src= "" onerror="this.onerror=null,this.src=&quot;/img/404.jpg&quot;" data-lazy-src="https://www.bing.com/th?id=OHR.BrockenSpecter_EN-IN4945372418_UHD.jpg" onerror='onerror=null,src="/img/404.jpg"' alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">一、RabbitMQ-客户端源码之ConnectionFactory</div></div></a></div></nav><hr><div id="post-comment"><div class="comment-head"><div class="comment-headline"><i class="anzhiyufont anzhiyu-icon-comments"></i><span> 评论</span></div><div class="comment-randomInfo"><a onclick="anzhiyu.addRandomCommentInfo()" href="javascript:void(0)" rel="external nofollow noreferrer">匿名评论</a><a href="/privacy" style="margin-left:4px">隐私政策</a></div><div class="comment-switch"><span class="first-comment">Twikoo</span><span id="switch-btn"></span><span class="second-comment">Artalk</span></div><div class="comment-tips" id="comment-tips"><span>✅ 你无需删除空行，直接评论以获取最佳展示效果</span></div></div><div class="comment-wrap"><div><div id="twikoo-wrap"></div></div><div><div id="artalk-wrap"></div></div></div></div><div class="comment-barrage"></div></div><div class="aside-content" id="aside-content"><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="anzhiyufont anzhiyu-icon-bars"></i><span>文章目录</span><span class="toc-percentage"></span></div><div class="toc-content is-expand"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E4%B9%9D%E3%80%81Dubbo-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E2%80%93-%E6%9C%8D%E5%8A%A1%E8%B0%83%E7%94%A8%E8%BF%87%E7%A8%8B"><span class="toc-number">1.</span> <span class="toc-text">九、Dubbo 源码分析 – 服务调用过程</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#1-%E7%AE%80%E4%BB%8B"><span class="toc-number">1.1.</span> <span class="toc-text">1. 简介</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90"><span class="toc-number">1.2.</span> <span class="toc-text">2. 源码分析</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#2-1-%E6%9C%8D%E5%8A%A1%E8%B0%83%E7%94%A8%E6%96%B9%E5%BC%8F"><span class="toc-number">1.2.1.</span> <span class="toc-text">2.1 服务调用方式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-2-%E6%9C%8D%E5%8A%A1%E6%B6%88%E8%B4%B9%E6%96%B9%E5%8F%91%E9%80%81%E8%AF%B7%E6%B1%82"><span class="toc-number">1.2.2.</span> <span class="toc-text">2.2 服务消费方发送请求</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#2-2-1-%E5%8F%91%E9%80%81%E8%AF%B7%E6%B1%82"><span class="toc-number">1.2.2.1.</span> <span class="toc-text">2.2.1 发送请求</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-2-2-%E8%AF%B7%E6%B1%82%E7%BC%96%E7%A0%81"><span class="toc-number">1.2.2.2.</span> <span class="toc-text">2.2.2 请求编码</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-%E6%9C%8D%E5%8A%A1%E6%8F%90%E4%BE%9B%E6%96%B9%E6%8E%A5%E6%94%B6%E8%AF%B7%E6%B1%82"><span class="toc-number">1.2.3.</span> <span class="toc-text">2.3 服务提供方接收请求</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#2-3-1-%E8%AF%B7%E6%B1%82%E8%A7%A3%E7%A0%81"><span class="toc-number">1.2.3.1.</span> <span class="toc-text">2.3.1 请求解码</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-3-2-%E8%B0%83%E7%94%A8%E6%9C%8D%E5%8A%A1"><span class="toc-number">1.2.3.2.</span> <span class="toc-text">2.3.2 调用服务</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#2-3-2-1-%E7%BA%BF%E7%A8%8B%E6%B4%BE%E5%8F%91%E6%A8%A1%E5%9E%8B"><span class="toc-number">1.2.3.2.1.</span> <span class="toc-text">2.3.2.1 线程派发模型</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#2-3-2-2-%E8%B0%83%E7%94%A8%E6%9C%8D%E5%8A%A1"><span class="toc-number">1.2.3.2.2.</span> <span class="toc-text">2.3.2.2 调用服务</span></a></li></ol></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-4-%E6%9C%8D%E5%8A%A1%E6%8F%90%E4%BE%9B%E6%96%B9%E5%93%8D%E5%BA%94%E8%AF%B7%E6%B1%82"><span class="toc-number">1.2.4.</span> <span class="toc-text">2.4 服务提供方响应请求</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-5-%E6%9C%8D%E5%8A%A1%E6%B6%88%E8%B4%B9%E6%96%B9%E6%8E%A5%E6%94%B6%E8%B0%83%E7%94%A8%E7%BB%93%E6%9E%9C"><span class="toc-number">1.2.5.</span> <span class="toc-text">2.5 服务消费方接收调用结果</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#2-5-1-%E5%93%8D%E5%BA%94%E6%95%B0%E6%8D%AE%E8%A7%A3%E7%A0%81"><span class="toc-number">1.2.5.1.</span> <span class="toc-text">2.5.1 响应数据解码</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-5-2-%E5%90%91%E7%94%A8%E6%88%B7%E7%BA%BF%E7%A8%8B%E4%BC%A0%E9%80%92%E8%B0%83%E7%94%A8%E7%BB%93%E6%9E%9C"><span class="toc-number">1.2.5.2.</span> <span class="toc-text">2.5.2 向用户线程传递调用结果</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-%E6%80%BB%E7%BB%93"><span class="toc-number">1.3.</span> <span class="toc-text">3. 总结</span></a></li></ol></li></ol></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div id="footer_deal"><a class="deal_link" href="mailto:2071916845@qq.com" rel="external nofollow noreferrer" title="email"><i class="anzhiyufont anzhiyu-icon-envelope"></i></a><a class="deal_link" target="_blank" rel="noopener external nofollow noreferrer" href="https://weibo.com/" title="微博"><i class="anzhiyufont anzhiyu-icon-weibo"></i></a><a class="deal_link" target="_blank" rel="noopener external nofollow noreferrer" href="https://www.facebook.com/" title="facebook"><i class="anzhiyufont anzhiyu-icon-facebook1"></i></a><a class="deal_link" href="/atom.xml" title="RSS"><i class="anzhiyufont anzhiyu-icon-rss"></i></a><img class="footer_mini_logo" title="返回顶部" alt="返回顶部" onclick="anzhiyu.scrollToDest(0,500)" src= "" onerror="this.onerror=null,this.src=&quot;/img/404.jpg&quot;" data-lazy-src="https://q1.qlogo.cn/g?b=qq&amp;nk=2071916845&amp;s=640" size="50px"><a class="deal_link" target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/it985" title="Github"><i class="anzhiyufont anzhiyu-icon-github"></i></a><a class="deal_link" target="_blank" rel="noopener external nofollow noreferrer" href="https://space.bilibili.com/300767383" title="Bilibili"><i class="anzhiyufont anzhiyu-icon-bilibili"></i></a><a class="deal_link" target="_blank" rel="noopener external nofollow noreferrer" href="https://v.douyin.com/" title="抖音"><i class="anzhiyufont anzhiyu-icon-tiktok"></i></a><a class="deal_link" href="/copyright" title="CC"><i class="anzhiyufont anzhiyu-icon-copyright-line"></i></a></div><div id="anzhiyu-footer"><div class="footer-group"><div class="footer-title">服务</div><div class="footer-links"><a class="footer-item" title="51la统计" target="_blank" rel="noopener external nofollow noreferrer" href="https://v6.51.la/">51la统计</a><a class="footer-item" title="十年之约" target="_blank" rel="noopener external nofollow noreferrer" href="https://www.foreverblog.cn/">十年之约</a><a class="footer-item" title="开往" target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/travellings-link/travellings">开往</a></div></div><div class="footer-group"><div class="footer-title">导航</div><div class="footer-links"><a class="footer-item" title="即刻短文" href="/essay/">即刻短文</a><a class="footer-item" title="友链文章" href="/fcircle/">友链文章</a><a class="footer-item" title="留言板" href="/comments/">留言板</a></div></div><div class="footer-group"><div class="footer-title">协议</div><div class="footer-links"><a class="footer-item" title="免责声明" href="/disclaimer/">免责声明</a><a class="footer-item" title="隐私协议" href="/privacy/">隐私协议</a><a class="footer-item" title="Cookies" href="/cookies/">Cookies</a><a class="footer-item" title="版权协议" href="/copyright/">版权协议</a></div></div><div class="footer-group"><div class="footer-title">娱乐</div><div class="footer-links"><a class="footer-item" title="小空调" href="/air-conditioner/">小空调</a><a class="footer-item" title="围住小猫" href="/catch-cat/">围住小猫</a><a class="footer-item" title="免费图床" href="/huluxiapicture/">免费图床</a></div></div><div class="footer-group"><div class="footer-title">推荐分类</div><div class="footer-links"><a class="footer-item" title="Java" href="/categories/Java/">Java</a><a class="footer-item" title="面试" href="/categories/%E9%9D%A2%E8%AF%95/">面试</a></div></div><div class="footer-group"><div class="footer-title-group"><div class="footer-title">友链</div><a class="random-friends-btn" id="footer-random-friends-btn" href="javascript:addFriendLinksInFooter();" rel="external nofollow noreferrer" title="换一批友情链接"><i class="anzhiyufont anzhiyu-icon-arrow-rotate-right"></i></a></div><div class="footer-links" id="friend-links-in-footer"></div></div></div><div id="workboard"><img class="workSituationImg boardsign" src= "" onerror="this.onerror=null,this.src=&quot;/img/404.jpg&quot;" data-lazy-src="https://npm.elemecdn.com/anzhiyu-blog@2.0.4/img/badge/安知鱼-上班摸鱼中.svg" alt="距离月入25k也就还差一个大佬带我~" title="距离月入25k也就还差一个大佬带我~"><div id="runtimeTextTip"></div></div><div class="wordcount"></div><span>云少已经写了 996.7k 字，</span><span>好像写完一本我国著名的 四大名著 了！！！</span><p id="ghbdages"><a class="github-badge" target="_blank" href="https://hexo.io/" rel="external nofollow noreferrer" style="margin-inline:5px" data-title="博客框架为Hexo_v5.4.0" title="博客框架为Hexo_v5.4.0"><img src= "" onerror="this.onerror=null,this.src=&quot;/img/404.jpg&quot;" data-lazy-src="https://npm.elemecdn.com/anzhiyu-blog@2.1.5/img/badge/Frame-Hexo.svg" alt="博客框架为Hexo_v5.4.0"></a><a class="github-badge" target="_blank" href="https://www.upyun.com/?utm_source=lianmeng&amp;utm_medium=referral" rel="external nofollow noreferrer" style="margin-inline:5px" data-title="本站使用又拍云为静态资源提供CDN加速" title="本站使用又拍云为静态资源提供CDN加速"><img src= "" onerror="this.onerror=null,this.src=&quot;/img/404.jpg&quot;" data-lazy-src="https://img.shields.io/badge/CDN-%E5%8F%88%E6%8B%8D%E4%BA%91-orange%3Fstyle%3Dflat%26logo%3D%E5%8F%88%E6%8B%8D%E4%BA%91" alt="本站使用又拍云为静态资源提供CDN加速"></a><a class="github-badge" target="_blank" href="https://github.com/" rel="external nofollow noreferrer" style="margin-inline:5px" data-title="本站项目由Github托管" title="本站项目由Github托管"><img src= "" onerror="this.onerror=null,this.src=&quot;/img/404.jpg&quot;" data-lazy-src="https://npm.elemecdn.com/anzhiyu-blog@2.1.5/img/badge/Source-Github.svg" alt="本站项目由Github托管"></a><a class="github-badge" target="_blank" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" rel="external nofollow noreferrer" style="margin-inline:5px" data-title="本站采用知识共享署名-非商业性使用-相同方式共享4.0国际许可协议进行许可" title="本站采用知识共享署名-非商业性使用-相同方式共享4.0国际许可协议进行许可"><img src= "" onerror="this.onerror=null,this.src=&quot;/img/404.jpg&quot;" data-lazy-src="https://npm.elemecdn.com/anzhiyu-blog@2.2.0/img/badge/Copyright-BY-NC-SA.svg" alt="本站采用知识共享署名-非商业性使用-相同方式共享4.0国际许可协议进行许可"></a><a class="github-badge" target="_blank" href="https://icp.gov.moe/?keyword=20221607" rel="external nofollow noreferrer" style="margin-inline:5px" data-title="萌ICP备20221607号" title="萌ICP备20221607号"><img src= "" onerror="this.onerror=null,this.src=&quot;/img/404.jpg&quot;" data-lazy-src="https://img.shields.io/badge/%E8%90%8CICP%E5%A4%87-20221607-fe1384?style-flat&amp;logo=" alt="萌ICP备20221607号"></a></p></div><div id="footer-bar"><div class="footer-bar-links"><div class="footer-bar-left"><div id="footer-bar-tips"><div class="copyright">&copy;2018 - 2023 By <a class="footer-bar-link" href="/" title="云少" target="_blank">云少</a></div></div><div id="footer-type-tips"></div><div class="js-pjax"><script>function subtitleType(){fetch("https://v1.hitokoto.cn").then(t=>t.json()).then(t=>{var e="出自 "+t.from,p=[];p.unshift(t.hitokoto,e),window.typed=new Typed("#footer-type-tips",{strings:p,startDelay:300,typeSpeed:150,loop:!0,backSpeed:50})})}"function"==typeof Typed?subtitleType():getScript("https://cdn.cbd.int/typed.js@2.0.15/dist/typed.umd.js").then(subtitleType)</script></div></div><div class="footer-bar-right"><a class="footer-bar-link" target="_blank" rel="noopener external nofollow noreferrer" href="https://www.tryrun.top" title="云少">云少</a><a class="footer-bar-link" target="_blank" rel="noopener external nofollow noreferrer" href="https://beian.miit.gov.cn/" title="鄂ICP备2021021095号-2">鄂ICP备2021021095号-2</a><a class="footer-bar-link cc" href="/copyright" title="cc协议"><i class="anzhiyufont anzhiyu-icon-copyright-line"></i><i class="anzhiyufont anzhiyu-icon-creative-commons-by-line"></i><i class="anzhiyufont anzhiyu-icon-creative-commons-nc-line"></i><i class="anzhiyufont anzhiyu-icon-creative-commons-nd-line"></i></a></div></div></div></footer></div><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="sidebar-site-data site-data is-center"><a href="/archives/" title="archive"><div class="headline">文章</div><div class="length-num">861</div></a><a href="/tags/" title="tag"><div class="headline">标签</div><div class="length-num">35</div></a><a href="/categories/" title="category"><div class="headline">分类</div><div class="length-num">6</div></a></div><span class="sidebar-menu-item-title">功能</span><div class="sidebar-menu-item"><a class="darkmode_switchbutton menu-child" href="javascript:void(0);" rel="external nofollow noreferrer" title="显示模式"><i class="anzhiyufont anzhiyu-icon-circle-half-stroke"></i><span>显示模式</span></a></div><div class="back-menu-list-groups"><div class="back-menu-list-group"><div class="back-menu-list-title">网页</div><div class="back-menu-list"><a class="back-menu-item" target="_blank" rel="noopener external nofollow noreferrer" href="https://www.tryrun.top/" title="博客"><img class="back-menu-item-icon" src= "" onerror="this.onerror=null,this.src=&quot;/img/404.jpg&quot;" data-lazy-src="/img/favicon.ico" alt="博客"><span class="back-menu-item-text">博客</span></a></div></div><div class="back-menu-list-group"><div class="back-menu-list-title">项目</div><div class="back-menu-list"><a class="back-menu-item" target="_blank" rel="noopener external nofollow noreferrer" href="https://www.tryrun.top" title="图床"><img class="back-menu-item-icon" src= "" onerror="this.onerror=null,this.src=&quot;/img/404.jpg&quot;" data-lazy-src="https://www.tryrun.top/favicon.ico" alt="图床"><span class="back-menu-item-text">图床</span></a></div></div></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="javascript:void(0);" rel="external nofollow noreferrer"><span>望四方</span></a><ul class="menus_item_child"><li><a class="site-page child faa-parent animated-hover" href="/archives/"><i class="anzhiyufont anzhiyu-icon-box-archive faa-tada" style="font-size:.9em"></i><span> 归名档</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/categories/"><i class="anzhiyufont anzhiyu-icon-shapes faa-tada" style="font-size:.9em"></i><span> 归四类</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/tags/"><i class="anzhiyufont anzhiyu-icon-tags faa-tada" style="font-size:.9em"></i><span> 书中签</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/charts/"><i class="fa-fw fas fa-chart-bar faa-tada"></i><span> 统计</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="javascript:void(0);" rel="external nofollow noreferrer"><span>友链</span></a><ul class="menus_item_child"><li><a class="site-page child faa-parent animated-hover" href="/link/"><i class="anzhiyufont anzhiyu-icon-link faa-tada" style="font-size:.9em"></i><span> 四方好友</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/fcircle/"><i class="anzhiyufont anzhiyu-icon-artstation faa-tada" style="font-size:.9em"></i><span> 朋友圈</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/comments/"><i class="anzhiyufont anzhiyu-icon-envelope faa-tada" style="font-size:.9em"></i><span> 留言板</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="javascript:void(0);" rel="external nofollow noreferrer"><span>我的</span></a><ul class="menus_item_child"><li><a class="site-page child faa-parent animated-hover" href="/bangumis/"><i class="anzhiyufont anzhiyu-icon-bilibili faa-tada" style="font-size:.9em"></i><span> 追番页</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/album/"><i class="anzhiyufont anzhiyu-icon-images faa-tada" style="font-size:.9em"></i><span> 刹那间</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/equipment/"><i class="fas fa-heart faa-tada"></i><span> 我的装备</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/collect/"><i class="fas fa-camcorder faa-tada"></i><span> 观影阁</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="javascript:void(0);" rel="external nofollow noreferrer"><span>关于</span></a><ul class="menus_item_child"><li><a class="site-page child faa-parent animated-hover" href="/about/"><i class="anzhiyufont anzhiyu-icon-paper-plane faa-tada" style="font-size:.9em"></i><span> 关于本人</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/essay/"><i class="anzhiyufont anzhiyu-icon-lightbulb faa-tada" style="font-size:.9em"></i><span> 闲言碎语</span></a></li><li><a class="site-page child faa-parent animated-hover" href="javascript:toRandomPost()" rel="external nofollow noreferrer"><i class="anzhiyufont anzhiyu-icon-shoe-prints1 faa-tada" style="font-size:.9em"></i><span> 随便逛逛</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/disclaimer/"><i class="fas fa-heart faa-tada"></i><span> 免责声明</span></a></li><li><a class="site-page child faa-parent animated-hover" href="/love/"><i class="anzhiyufont anzhiyu-icon-heartbeat faa-tada" style="font-size:.9em"></i><span> 恋爱小屋</span></a></li></ul></div></div><span class="sidebar-menu-item-title">标签</span><div class="card-tags"><div class="item-headline"></div><div class="card-tag-cloud"><a href="/tags/API/" style="font-size:.88rem;color:#758692">API<sup>43</sup></a><a href="/tags/Base64/" style="font-size:.88rem;color:#2584bf">Base64<sup>1</sup></a><a href="/tags/Collectors/" style="font-size:.88rem;color:#bfad3e">Collectors<sup>3</sup></a><a href="/tags/Date/" style="font-size:.88rem;color:#b9b609">Date<sup>3</sup></a><a href="/tags/Executor/" style="font-size:.88rem;color:#14c83c">Executor<sup>9</sup></a><a href="/tags/Guava/" style="font-size:.88rem;color:#0a1878">Guava<sup>1</sup></a><a href="/tags/JVM/" style="font-size:.88rem;color:#625421">JVM<sup>8</sup></a><a href="/tags/Java8/" style="font-size:.88rem;color:#aa71bf">Java8<sup>21</sup></a><a href="/tags/Java9/" style="font-size:.88rem;color:#ad8774">Java9<sup>21</sup></a><a href="/tags/Java%E5%B9%B6%E5%8F%91/" style="font-size:.88rem;color:#19704c">Java并发<sup>20</sup></a><a href="/tags/Lambda/" style="font-size:.88rem;color:#7e5c78">Lambda<sup>4</sup></a><a href="/tags/Lock/" style="font-size:.88rem;color:#700769">Lock<sup>1</sup></a><a href="/tags/Maven/" style="font-size:.88rem;color:#2b8671">Maven<sup>1</sup></a><a href="/tags/Memcached/" style="font-size:.88rem;color:#1b0694">Memcached<sup>23</sup></a><a href="/tags/Mongodb/" style="font-size:.88rem;color:#36202d">Mongodb<sup>49</sup></a><a href="/tags/Queue/" style="font-size:.88rem;color:#643f76">Queue<sup>1</sup></a><a href="/tags/Redis/" style="font-size:.88rem;color:#1e4c38">Redis<sup>27</sup></a><a href="/tags/Stream/" style="font-size:.88rem;color:#a5a097">Stream<sup>4</sup></a><a href="/tags/Thread/" style="font-size:.88rem;color:#05682d">Thread<sup>7</sup></a><a href="/tags/Thread-pool/" style="font-size:.88rem;color:#38ad2a">Thread pool<sup>7</sup></a><a href="/tags/forkJoinPool/" style="font-size:.88rem;color:#827a6b">forkJoinPool<sup>2</sup></a><a href="/tags/stream/" style="font-size:.88rem;color:#4a3ac7">stream<sup>1</sup></a><a href="/tags/%E4%B8%93%E6%A0%8F/" style="font-size:.88rem;color:#b89c14">专栏<sup>35</sup></a><a href="/tags/%E4%BA%8B%E5%8A%A1/" style="font-size:.88rem;color:#922401">事务<sup>1</sup></a><a href="/tags/%E4%BC%98%E5%8C%96/" style="font-size:.88rem;color:#437d09">优化<sup>1</sup></a><a href="/tags/%E5%91%BD%E4%BB%A4/" style="font-size:.88rem;color:#705b06">命令<sup>57</sup></a><a href="/tags/%E5%AE%89%E8%A3%85/" style="font-size:.88rem;color:#06a544">安装<sup>6</sup></a><a href="/tags/%E5%B7%A5%E5%85%B7/" style="font-size:.88rem;color:#c86c4f">工具<sup>2</sup></a><a href="/tags/%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B/" style="font-size:.88rem;color:#560b2c">数据类型<sup>8</sup></a><a href="/tags/%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/" style="font-size:.88rem;color:#4e0a32">生命周期<sup>1</sup></a><a href="/tags/%E7%AE%80%E4%BB%8B/" style="font-size:.88rem;color:#230f8e">简介<sup>7</sup></a><a href="/tags/%E7%AE%97%E6%B3%95/" style="font-size:.88rem;color:#c0771e">算法<sup>10</sup></a><a href="/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" style="font-size:.88rem;color:#b40caa">设计模式<sup>38</sup></a><a href="/tags/%E9%85%8D%E7%BD%AE/" style="font-size:.88rem;color:#67389a">配置<sup>2</sup></a><a href="/tags/%E9%9D%A2%E8%AF%95/" style="font-size:.88rem;color:#57425c">面试<sup>11</sup></a></div></div><hr></div></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="anzhiyufont anzhiyu-icon-book-open"></i></button><button id="translateLink" type="button" title="简繁转换">繁</button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="anzhiyufont anzhiyu-icon-circle-half-stroke"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="anzhiyufont anzhiyu-icon-arrows-left-right"></i></button></div><div id="rightside-config-show"><button id="rightside-config" type="button" title="设置"><i class="anzhiyufont anzhiyu-icon-gear"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="anzhiyufont anzhiyu-icon-list-ul"></i></button><a id="to_comment" href="#post-comment" title="直达评论"><i class="anzhiyufont anzhiyu-icon-comments"></i></a><button type="button" title="切换背景" onclick="toggleWinbox()"><i class="fas fa-display"></i></button><button id="go-up" type="button" title="回到顶部"><i class="anzhiyufont anzhiyu-icon-arrow-up"></i></button></div></div><div id="algolia-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">搜索</span><button class="search-close-button"><i class="anzhiyufont anzhiyu-icon-xmark"></i></button></nav><div class="search-wrap"><div id="algolia-search-input"></div><hr><div id="algolia-search-results"><div id="algolia-hits"></div><div id="algolia-pagination"></div><div id="algolia-info"><div class="algolia-stats"></div><div class="algolia-poweredBy"></div></div></div></div></div><div id="search-mask"></div></div><div id="rightMenu"><div class="rightMenu-group rightMenu-small"><div class="rightMenu-item" id="menu-backward"><i class="anzhiyufont anzhiyu-icon-arrow-left"></i></div><div class="rightMenu-item" id="menu-forward"><i class="anzhiyufont anzhiyu-icon-arrow-right"></i></div><div class="rightMenu-item" id="menu-refresh"><i class="anzhiyufont anzhiyu-icon-arrow-rotate-right" style="font-size:1rem"></i></div><div class="rightMenu-item" id="menu-top"><i class="anzhiyufont anzhiyu-icon-arrow-up"></i></div></div><div class="rightMenu-group rightMenu-line rightMenuPlugin"><div class="rightMenu-item" id="menu-copytext"><i class="anzhiyufont anzhiyu-icon-copy"></i><span>复制选中文本</span></div><div class="rightMenu-item" id="menu-pastetext"><i class="anzhiyufont anzhiyu-icon-paste"></i><span>粘贴文本</span></div><a class="rightMenu-item" id="menu-commenttext"><i class="anzhiyufont anzhiyu-icon-comment-medical"></i><span>引用到评论</span></a><div class="rightMenu-item" id="menu-newwindow"><i class="anzhiyufont anzhiyu-icon-window-restore"></i><span>新窗口打开</span></div><div class="rightMenu-item" id="menu-copylink"><i class="anzhiyufont anzhiyu-icon-link"></i><span>复制链接地址</span></div><div class="rightMenu-item" id="menu-copyimg"><i class="anzhiyufont anzhiyu-icon-images"></i><span>复制此图片</span></div><div class="rightMenu-item" id="menu-downloadimg"><i class="anzhiyufont anzhiyu-icon-download"></i><span>下载此图片</span></div><div class="rightMenu-item" id="menu-newwindowimg"><i class="anzhiyufont anzhiyu-icon-window-restore"></i><span>新窗口打开图片</span></div><div class="rightMenu-item" id="menu-search"><i class="anzhiyufont anzhiyu-icon-magnifying-glass"></i><span>站内搜索</span></div><div class="rightMenu-item" id="menu-searchBaidu"><i class="anzhiyufont anzhiyu-icon-magnifying-glass"></i><span>百度搜索</span></div><div class="rightMenu-item" id="menu-music-toggle"><i class="anzhiyufont anzhiyu-icon-play"></i><span>播放音乐</span></div><div class="rightMenu-item" id="menu-music-back"><i class="anzhiyufont anzhiyu-icon-backward"></i><span>切换到上一首</span></div><div class="rightMenu-item" id="menu-music-forward"><i class="anzhiyufont anzhiyu-icon-forward"></i><span>切换到下一首</span></div><div class="rightMenu-item" id="menu-music-playlist" onclick="window.open(&quot;https://y.qq.com/n/ryqq/playlist/8802438608&quot;, &quot;_blank&quot;);" style="display:none"><i class="anzhiyufont anzhiyu-icon-radio"></i><span>查看所有歌曲</span></div><div class="rightMenu-item" id="menu-music-copyMusicName"><i class="anzhiyufont anzhiyu-icon-copy"></i><span>复制歌名</span></div></div><div class="rightMenu-group rightMenu-line rightMenuOther"><a class="rightMenu-item menu-link" id="menu-randomPost"><i class="anzhiyufont anzhiyu-icon-shuffle"></i><span>随便逛逛</span></a><a class="rightMenu-item menu-link" href="/categories/"><i class="anzhiyufont anzhiyu-icon-cube"></i><span>博客分类</span></a><a class="rightMenu-item menu-link" href="/tags/"><i class="anzhiyufont anzhiyu-icon-tags"></i><span>文章标签</span></a></div><div class="rightMenu-group rightMenu-line rightMenuOther"><a class="rightMenu-item" id="menu-copy" href="javascript:void(0);" rel="external nofollow noreferrer"><i class="anzhiyufont anzhiyu-icon-copy"></i><span>复制地址</span></a><a class="rightMenu-item" id="menu-commentBarrage" href="javascript:void(0);" rel="external nofollow noreferrer"><i class="anzhiyufont anzhiyu-icon-message"></i><span class="menu-commentBarrage-text">关闭热评</span></a><a class="rightMenu-item" id="menu-darkmode" href="javascript:void(0);" rel="external nofollow noreferrer"><i class="anzhiyufont anzhiyu-icon-circle-half-stroke"></i><span class="menu-darkmode-text">深色模式</span></a><a class="rightMenu-item" id="menu-translate" href="javascript:void(0);" rel="external nofollow noreferrer"><i class="anzhiyufont anzhiyu-icon-language"></i><span>轉為繁體</span></a></div></div><div id="rightmenu-mask"></div><div id="he-plugin-simple"></div><script>var WIDGET={CONFIG:{modules:"0124",background:"2",tmpColor:"FFFFFF",tmpSize:"16",cityColor:"FFFFFF",citySize:"16",aqiColor:"E8D87B",aqiSize:"16",weatherIconSize:"24",alertIconSize:"18",padding:"10px 10px 10px 10px",shadow:"0",language:"auto",borderRadius:"20",fixed:"true",vertical:"top",horizontal:"left",left:"20",top:"7.1",key:"df245676fb434a0691ead1c63341cd94"}}</script><link rel="stylesheet" href="https://widget.qweather.net/simple/static/css/he-simple.css?v=1.4.0"><script src="https://widget.qweather.net/simple/static/js/he-simple.js?v=1.4.0"></script><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="/js/tw_cn.js"></script><script src="https://cdn.cbd.int/@fancyapps/ui@5.0.20/dist/fancybox/fancybox.umd.js"></script><script src="https://cdn.cbd.int/instant.page@5.2.0/instantpage.js" type="module"></script><script src="https://cdn.cbd.int/vanilla-lazyload@17.8.4/dist/lazyload.iife.min.js"></script><script src="https://cdn.cbd.int/node-snackbar@0.1.16/dist/snackbar.min.js"></script><script>function panguFn(){"object"==typeof pangu?pangu.autoSpacingPage():getScript("https://cdn.cbd.int/pangu@4.0.7/dist/browser/pangu.min.js").then(()=>{pangu.autoSpacingPage()})}function panguInit(){panguFn()}document.addEventListener("DOMContentLoaded",panguInit)</script><script>var meting_api="https://api.injahow.cn/meting/?server=:server&type=:type&id=:id&auth=:auth&r=:r"</script><canvas id="universe"></canvas><script async src="https://npm.elemecdn.com/anzhiyu-theme-static@1.0.0/dark/dark.js"></script><script>var HoldLog=console.log;console.log=function(){};let now1=new Date;queueMicrotask(()=>{function o(){HoldLog.apply(console,arguments)}var c=new Date("09/01/2018 00:00:00"),c=(now1.setTime(now1.getTime()+250),(now1-c)/1e3/60/60/24),c=["欢迎使用安知鱼!","生活明朗, 万物可爱",`
        
       █████╗ ███╗   ██╗███████╗██╗  ██╗██╗██╗   ██╗██╗   ██╗
      ██╔══██╗████╗  ██║╚══███╔╝██║  ██║██║╚██╗ ██╔╝██║   ██║
      ███████║██╔██╗ ██║  ███╔╝ ███████║██║ ╚████╔╝ ██║   ██║
      ██╔══██║██║╚██╗██║ ███╔╝  ██╔══██║██║  ╚██╔╝  ██║   ██║
      ██║  ██║██║ ╚████║███████╗██║  ██║██║   ██║   ╚██████╔╝
      ╚═╝  ╚═╝╚═╝  ╚═══╝╚══════╝╚═╝  ╚═╝╚═╝   ╚═╝    ╚═════╝
        
        `,"已上线",Math.floor(c),"天","©2018 By 安知鱼 V1.6.6"],e=["NCC2-036","调用前置摄像头拍照成功，识别为【小笨蛋】.","Photo captured: ","🤪"];setTimeout(o.bind(console,`
%c${c[0]} %c ${c[1]} %c ${c[2]} %c${c[3]}%c ${c[4]}%c ${c[5]}

%c ${c[6]}
`,"color:#425AEF","","color:#425AEF","color:#425AEF","","color:#425AEF","")),setTimeout(o.bind(console,`%c ${e[0]} %c ${e[1]} %c 
${e[2]} %c
${e[3]}
`,"color:white; background-color:#4fd953","","",'background:url("https://npm.elemecdn.com/anzhiyu-blog@1.1.6/img/post/common/tinggge.gif") no-repeat;font-size:450%')),setTimeout(o.bind(console,"%c WELCOME %c 你好，小笨蛋.","color:white; background-color:#4f90d9","")),setTimeout(console.warn.bind(console,"%c ⚡ Powered by 安知鱼 %c 你正在访问 云少 的博客.","color:white; background-color:#f0ad4e","")),setTimeout(o.bind(console,"%c W23-12 %c 你已打开控制台.","color:white; background-color:#4f90d9","")),setTimeout(console.warn.bind(console,"%c S013-782 %c 你现在正处于监控中.","color:white; background-color:#d9534f",""))})</script><script async src="/anzhiyu/random.js"></script><script async>!function(){var n,o,r,a,i,e=new Date("09/01/2018 00:00:00"),l=new Date;setInterval(()=>{var t;if(l=new Date,i=l.getHours(),t=(l-e)/1e3/60/60/24,n=Math.floor(t),t=(l-e)/1e3/60/60-24*n,o=Math.floor(t),1==String(o).length&&(o="0"+o),t=(l-e)/1e3/60-1440*n-60*o,r=Math.floor(t),1==String(r).length&&(r="0"+r),t=(l-e)/1e3-86400*n-3600*o-60*r,a=Math.round(t),1==String(a).length&&(a="0"+a),document.getElementById("footer")){let e="";e=(i<18&&9<=i||((t=document.querySelector("#workboard .workSituationImg")).src="https://npm.elemecdn.com/anzhiyu-blog@2.0.4/img/badge/安知鱼-下班啦.svg",t.title="下班了就该开开心心的玩耍，嘿嘿~",t.alt="下班了就该开开心心的玩耍，嘿嘿~"),`本站居然运行了 ${n} 天<span id='runtime'> ${o} 小时 ${r} 分 ${a} 秒 </span><i class='anzhiyufont anzhiyu-icon-heartbeat' style='color:red'></i>`),document.getElementById("runtimeTextTip")&&(document.getElementById("runtimeTextTip").innerHTML=e)}},1e3)}()</script><script src="https://cdn.cbd.int/algoliasearch@4.18.0/dist/algoliasearch-lite.umd.js"></script><script src="https://cdn.cbd.int/instantsearch.js@4.56.5/dist/instantsearch.production.min.js"></script><script src="/js/search/algolia.js"></script><div class="js-pjax"><script>(()=>{const e=document.querySelectorAll("#article-container .mermaid-wrap");if(0!==e.length){const n=()=>{window.loadMermaid=!0;const a="dark"===document.documentElement.getAttribute("data-theme")?"dark":"default";Array.from(e).forEach((e,t)=>{const n=e.firstElementChild;e="%%{init:{ 'theme':'"+a+"'}}%%\n"+n.textContent;const d=mermaid.render("mermaid-"+t,e);"string"==typeof d?(t=d,n.insertAdjacentHTML("afterend",t)):d.then(({svg:e})=>{n.insertAdjacentHTML("afterend",e)})})};var t=()=>{window.loadMermaid?n():getScript("https://cdn.cbd.int/mermaid@10.2.4/dist/mermaid.min.js").then(n)};anzhiyu.addGlobalFn("themeChange",n,"mermaid"),window.pjax?t():document.addEventListener("DOMContentLoaded",t)}})()</script><script>(()=>{const t=()=>{twikoo.init(Object.assign({el:"#twikoo-wrap",envId:"https://twikoo.tryrun.top/",region:"",onCommentLoaded:()=>{anzhiyu.loadLightbox(document.querySelectorAll("#twikoo .tk-content img:not(.tk-owo-emotion)"))}},null))};var o=()=>{"object"==typeof twikoo?setTimeout(n,0):getScript("https://cdn.cbd.int/twikoo@1.6.18/dist/twikoo.all.min.js").then(n)};const n=()=>{t()};anzhiyu.loadComment(document.getElementById("twikoo-wrap"),o)})()</script><script>(()=>{const t=()=>{window.artalkItem=new Artalk(Object.assign({el:"#artalk-wrap",server:"https://artalk.tryrun.top/",site:"https://blog.tryrun.top/",pageKey:location.pathname,darkMode:"dark"===document.documentElement.getAttribute("data-theme"),countEl:".artalk-count"},null)),"null"!==GLOBAL_CONFIG.lightbox&&window.artalkItem.use(t=>{t.on("list-loaded",()=>{t.getCommentList().forEach(t=>{t=t.getRender().$content;anzhiyu.loadLightbox(t.querySelectorAll("img:not([atk-emoticon])"))})})})};var a=async()=>{"object"==typeof window.artalkItem||(await getCSS("https://cdn.cbd.int/artalk@2.5.5/dist/Artalk.css"),await getScript("https://cdn.cbd.int/artalk@2.5.5/dist/Artalk.js")),t()};anzhiyu.addGlobalFn("themeChange",t=>{var a=document.getElementById("artalk-wrap");a&&a.children.length&&window.artalkItem.setDarkMode("dark"===t)},"artalk"),window.loadOtherComment=a})()</script><input type="hidden" name="page-type" id="page-type" value="post"></div><script>window.addEventListener("load",()=>{const t=e=>e=""!==e&&150<(e=(e=(e=(e=e.replace(/<img.*?src="(.*?)"?[^\>]+>/gi,"[图片]")).replace(/<a[^>]+?href=["']?([^"']+)["']?[^>]*>([^<]+)<\/a>/gi,"[链接]")).replace(/<pre><code>.*?<\/pre>/gi,"[代码]")).replace(/<[^>]+>/g,"")).length?e.substring(0,150)+"...":e,n=t=>{let n="";if(t.length)for(let e=0;e<t.length;e++)n=(n=(n+="<div class='aside-list-item'>")+`<a href='${t[e].url}' class='thumbnail'><img data-lazy-src='${t[e].avatar}' alt='${t[e].nick}'><div class='name'><span>${t[e].nick} </span></div></a>`)+`<div class='content'>
        <a class='comment' href='${t[e].url}' title='${t[e].content}'>${t[e].content}</a>
        <time datetime="${t[e].date}">${anzhiyu.diffDate(t[e].date,!0)}</time></div>
        </div>`;else n+="没有评论";var e=document.querySelector("#card-newest-comments .aside-list");e.innerHTML=n,window.lazyLoadInstance&&window.lazyLoadInstance.update(),window.pjax&&window.pjax.refresh(e)};var e=()=>{var e;document.querySelector("#card-newest-comments .aside-list")&&((e=saveToLocal.get("twikoo-newest-comments"))?n(JSON.parse(e)):(e=()=>{twikoo.getRecentComments({envId:"https://twikoo.tryrun.top/",region:"",pageSize:6,includeReply:!0}).then(function(e){e=e.map(e=>({content:t(e.comment),avatar:e.avatar,nick:e.nick,url:e.url+"#"+e.id,date:new Date(e.created).toISOString()}));saveToLocal.set("twikoo-newest-comments",JSON.stringify(e),10/1440),n(e)}).catch(function(e){document.querySelector("#card-newest-comments .aside-list").textContent="无法获取评论，请确认相关配置是否正确"})},"object"==typeof twikoo?e():getScript("https://cdn.cbd.int/twikoo@1.6.18/dist/twikoo.all.min.js").then(e)))};e(),document.addEventListener("pjax:complete",e)})</script><script async data-pjax src="https://npm.elemecdn.com/anzhiyu-theme-static@1.0.1/bubble/bubble.js"></script><script>var visitorMail="visitor@anheyu.com"</script><script async data-pjax src="https://cdn.cbd.int/anzhiyu-theme-static@1.0.0/waterfall/waterfall.js"></script><script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/qrcodejs/1.0.0/qrcode.min.js"></script><script src="/js/anzhiyu/right_click_menu.js"></script><link rel="stylesheet" href="https://cdn.cbd.int/anzhiyu-theme-static@1.1.9/icon/ali_iconfont_css.css"><script async src="/js/1.min.js?1"></script><script async src="/js/bg.js?1"></script><script async src="https://cdn1.tianli0.top/npm/sweetalert2@8.19.0/dist/sweetalert2.all.js"></script><script async src="/js/lunar.min.js?1"></script><script async src="/js/day.min.js?1"></script><script async src="/js/winbox.bundle.min.js?1"></script><script id="click-show-text" src="https://cdn.cbd.int/butterfly-extsrc@1.1.3/dist/click-show-text.min.js" data-mobile="true" data-text="富强,民主,文明,和谐,自由,平等,公正,法治,爱国,敬业,诚信,友善" data-fontsize="15px" data-random="true" async></script><link rel="stylesheet" href="https://cdn.cbd.int/anzhiyu-theme-static@1.0.0/aplayer/APlayer.min.css" media="print" onload='this.media="all"'><script src="https://cdn.cbd.int/anzhiyu-blog-static@1.0.1/js/APlayer.min.js"></script><script src="https://cdn.cbd.int/hexo-anzhiyu-music@1.0.1/assets/js/Meting2.min.js"></script><script src="https://cdn.cbd.int/pjax@0.2.8/pjax.min.js"></script><script>let pjaxSelectors=['meta[property="og:image"]','meta[property="og:title"]','meta[property="og:url"]','meta[property="og:type"]','meta[property="og:site_name"]','meta[property="og:description"]',"head > title","#config-diff","#body-wrap","#rightside-config-hide","#rightside-config-show",".js-pjax"];var pjax=new Pjax({elements:'a:not([target="_blank"])',selectors:pjaxSelectors,cacheBust:!1,analytics:!0,scrollRestoration:!1});document.addEventListener("pjax:send",function(){if(anzhiyu.removeGlobalFnEvent("pjax"),anzhiyu.removeGlobalFnEvent("themeChange"),document.getElementById("rightside").classList.remove("rightside-show"),window.aplayers)for(let e=0;e<window.aplayers.length;e++)window.aplayers[e].options.fixed||window.aplayers[e].destroy();"object"==typeof typed&&typed.destroy();var e=document.body.classList;e.contains("read-mode")&&e.remove("read-mode")}),document.addEventListener("pjax:complete",function(){window.refreshFn(),document.querySelectorAll("script[data-pjax]").forEach(e=>{const t=document.createElement("script");var a=e.text||e.textContent||e.innerHTML||"";Array.from(e.attributes).forEach(e=>t.setAttribute(e.name,e.value)),t.appendChild(document.createTextNode(a)),e.parentNode.replaceChild(t,e)}),GLOBAL_CONFIG.islazyload&&window.lazyLoadInstance.update(),"function"==typeof chatBtnFn&&chatBtnFn(),"function"==typeof panguInit&&panguInit(),"function"==typeof gtag&&gtag("config","G-3VMKW5TZBM",{page_path:window.location.pathname}),"object"==typeof _hmt&&_hmt.push(["_trackPageview",window.location.pathname]),"function"==typeof loadMeting&&document.getElementsByClassName("aplayer").length&&loadMeting(),"object"==typeof Prism&&Prism.highlightAll()}),document.addEventListener("pjax:error",e=>{404===e.request.status&&pjax.loadUrl("/404.html")})</script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><script charset="UTF-8" src="https://cdn.cbd.int/anzhiyu-theme-static@1.1.5/accesskey/accesskey.js"></script></div><div id="popup-window"><div class="popup-window-title">通知</div><div class="popup-window-divider"></div><div class="popup-window-content"><div class="popup-tip">你好呀</div><div class="popup-link"><i class="anzhiyufont anzhiyu-icon-arrow-circle-right"></i></div></div></div></body></html>